diff --git a/stm/Makefile b/stm/Makefile
index fecd525276986282a12b7b55dd8bb6505c5074a0..478441e04baa769ec0a698de40b2dcc74148994e 100644
--- a/stm/Makefile
+++ b/stm/Makefile
@@ -30,7 +30,8 @@ SRC_C = \
 	string0.c \
 	malloc0.c \
 	systick.c  \
-	lexerstm.c \
+	lexerstr.c \
+	lexerfatfs.c \
 	led.c \
 	lcd.c \
 	servo.c \
diff --git a/stm/lexerstm.c b/stm/lexerfatfs.c
similarity index 61%
rename from stm/lexerstm.c
rename to stm/lexerfatfs.c
index 661dfb016032d08562ec4eb6758998ee9d9a700a..5dcaca16062559eb6e1176c865b40023f82885cc 100644
--- a/stm/lexerstm.c
+++ b/stm/lexerfatfs.c
@@ -5,29 +5,7 @@
 
 #include "misc.h"
 #include "lexer.h"
-#include "lexerstm.h"
-
-unichar str_buf_next_char(mp_lexer_str_buf_t *sb) {
-    if (sb->src_cur < sb->src_end) {
-        return *sb->src_cur++;
-    } else {
-        return MP_LEXER_CHAR_EOF;
-    }
-}
-
-void str_buf_free(mp_lexer_str_buf_t *sb) {
-    if (sb->free) {
-        m_del(char, (char*)sb->src_beg, 0 /* don't know allocated size of src */);
-    }
-}
-
-mp_lexer_t *mp_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str, mp_lexer_str_buf_t *sb) {
-    sb->free = free_str;
-    sb->src_beg = str;
-    sb->src_cur = str;
-    sb->src_end = str + len;
-    return mp_lexer_new(src_name, sb, (mp_lexer_stream_next_char_t)str_buf_next_char, (mp_lexer_stream_close_t)str_buf_free);
-}
+#include "lexerfatfs.h"
 
 unichar file_buf_next_char(mp_lexer_file_buf_t *fb) {
     if (fb->pos >= fb->len) {
diff --git a/stm/lexerfatfs.h b/stm/lexerfatfs.h
new file mode 100644
index 0000000000000000000000000000000000000000..2b41ed136b51dba51380c5853c3851801b88675a
--- /dev/null
+++ b/stm/lexerfatfs.h
@@ -0,0 +1,8 @@
+typedef struct _py_lexer_file_buf_t {
+    FIL fp;
+    char buf[20];
+    uint16_t len;
+    uint16_t pos;
+} mp_lexer_file_buf_t;
+
+mp_lexer_t *mp_lexer_new_from_file(const char *filename, mp_lexer_file_buf_t *fb);
diff --git a/stm/lexerstm.h b/stm/lexerstm.h
deleted file mode 100644
index 7e090898a2fd2449b31ae2bb556e2cdf81ff32eb..0000000000000000000000000000000000000000
--- a/stm/lexerstm.h
+++ /dev/null
@@ -1,16 +0,0 @@
-typedef struct _py_lexer_str_buf_t {
-    bool free;                  // free src_beg when done
-    const char *src_beg;        // beginning of source
-    const char *src_cur;        // current location in source
-    const char *src_end;        // end (exclusive) of source
-} mp_lexer_str_buf_t;
-
-typedef struct _py_lexer_file_buf_t {
-    FIL fp;
-    char buf[20];
-    uint16_t len;
-    uint16_t pos;
-} mp_lexer_file_buf_t;
-
-mp_lexer_t *mp_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str, mp_lexer_str_buf_t *sb);
-mp_lexer_t *mp_lexer_new_from_file(const char *filename, mp_lexer_file_buf_t *fb);
diff --git a/teensy/lexerteensy.c b/stm/lexerstr.c
similarity index 85%
rename from teensy/lexerteensy.c
rename to stm/lexerstr.c
index eefaf66657554fc7abda8089391686e48e462674..cc6be1d1ed9f3cc91bc8bcf7131f445d8fb5292d 100644
--- a/teensy/lexerteensy.c
+++ b/stm/lexerstr.c
@@ -3,7 +3,7 @@
 
 #include "misc.h"
 #include "lexer.h"
-#include "lexerteensy.h"
+#include "lexerstr.h"
 
 unichar str_buf_next_char(mp_lexer_str_buf_t *sb) {
     if (sb->src_cur < sb->src_end) {
@@ -26,8 +26,3 @@ mp_lexer_t *mp_lexer_new_from_str_len(const char *src_name, const char *str, uin
     sb->src_end = str + len;
     return mp_lexer_new(src_name, sb, (mp_lexer_stream_next_char_t)str_buf_next_char, (mp_lexer_stream_close_t)str_buf_free);
 }
-
-mp_lexer_t *mp_import_open_file(qstr mod_name) {
-    printf("import not implemented\n");
-    return NULL;
-}
diff --git a/teensy/lexerteensy.h b/stm/lexerstr.h
similarity index 100%
rename from teensy/lexerteensy.h
rename to stm/lexerstr.h
diff --git a/stm/main.c b/stm/main.c
index a038c89b74a90ff18fd5c7fec85500ecb099c897..a2f3bc7f032cc6c8095aafb174e261d1d85cdb26 100644
--- a/stm/main.c
+++ b/stm/main.c
@@ -19,7 +19,8 @@
 #include "nlr.h"
 #include "misc.h"
 #include "lexer.h"
-#include "lexerstm.h"
+#include "lexerstr.h"
+#include "lexerfatfs.h"
 #include "parse.h"
 #include "obj.h"
 #include "compile.h"
diff --git a/teensy/Makefile b/teensy/Makefile
index 98b467a06e7a78cf790033d6bbe64dc392a6888e..f86585c65833670093eb8af89ecf17b30a11afcb 100644
--- a/teensy/Makefile
+++ b/teensy/Makefile
@@ -22,14 +22,19 @@ LIBS = -L $(COMPILER_PATH)/../lib/gcc/arm-none-eabi/4.7.2/thumb2 -lgcc
 
 SRC_C = \
 	main.c \
-	lexerteensy.c \
+	lcd.c \
 	led.c \
+	lexerfatfs.c \
+	usart.c \
+	usb.c \
+
+STM_SRC_C = \
+	lexerstr.c \
 	malloc0.c \
 	printf.c \
 	string0.c \
-	usb.c \
 
-SRC_S = \
+STM_SRC_S = \
 	gchelper.s \
 
 PY_O = \
@@ -90,7 +95,10 @@ SRC_TEENSY = \
 	usb_serial.c \
 	yield.c \
 
-OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(SRC_S:.s=.o) $(PY_O) $(SRC_TEENSY:.c=.o))
+STM_SRC_C_OBJ = $(STM_SRC_C:.c=.o)
+STM_SRC_S_OBJ = $(STM_SRC_S:.s=.o)
+
+OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(STM_SRC_C_OBJ) $(STM_SRC_S_OBJ) $(PY_O) $(SRC_TEENSY:.c=.o))
 #LIB = -lreadline
 # the following is needed for BSD
 #LIB += -ltermcap
@@ -117,10 +125,13 @@ $(PROG).elf: $(BUILD) $(OBJ)
 $(BUILD):
 	mkdir -p $@
 
-$(BUILD)/%.o: %.s
+$(BUILD)/%.o: %.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+$(BUILD)/%.o: ../stm/%.s
 	$(AS) -o $@ $<
 
-$(BUILD)/%.o: %.c
+$(BUILD)/%.o: ../stm/%.c
 	$(CC) $(CFLAGS) -c -o $@ $<
 
 $(BUILD)/%.o: $(PYSRC)/%.S
diff --git a/teensy/README.md b/teensy/README.md
index 31339cd7967efe01bcc86b6e5d454a23684def0e..2618abf2acba80ff883269e970c5fc92eab80197 100644
--- a/teensy/README.md
+++ b/teensy/README.md
@@ -8,13 +8,6 @@ cd teensy
 ARDUINO=~/arduino-1.0.5 make
 ```
 
-To build the loader
-
-```
-cd teensy/loader
-make
-```
-
 To upload micropython to the Teensy 3.1.
 
 Press the Program button on the Teensy 3.1
@@ -23,9 +16,3 @@ make upload
 ```
 
 Currently, the python prompt is through the USB serial interface.
-
-The LED will blink (100 msec on/100 msec off) while waiting for the USB Serial
-device to be configured, and will blink (200 msec on/200 msec off) while
-sitting at the readline prompt.
-
-Currently, there is no I/O support configured (no GPIO, ADC, etc).
diff --git a/teensy/gchelper.s b/teensy/gchelper.s
deleted file mode 100644
index f8735d28305943505b8c5664692fe31df0ef317f..0000000000000000000000000000000000000000
--- a/teensy/gchelper.s
+++ /dev/null
@@ -1,36 +0,0 @@
-    .syntax unified
-    .cpu cortex-m4
-    .thumb
-    .text
-    .align  2
-
-@ void gc_helper_get_regs_and_clean_stack(r0=uint regs[10], r1=heap_end)
-    .global gc_helper_get_regs_and_clean_stack
-    .thumb
-    .thumb_func
-    .type   gc_helper_get_regs_and_clean_stack, %function
-gc_helper_get_regs_and_clean_stack:
-    @ store registers into given array
-    str     r4, [r0], #4
-    str     r5, [r0], #4
-    str     r6, [r0], #4
-    str     r7, [r0], #4
-    str     r8, [r0], #4
-    str     r9, [r0], #4
-    str     r10, [r0], #4
-    str     r11, [r0], #4
-    str     r12, [r0], #4
-    str     r13, [r0], #4
-
-    @ clean the stack from given pointer up to current sp
-    movs    r0, #0
-    mov     r2, sp
-    b.n     .entry
-.loop:
-    str     r0, [r1], #4
-.entry:
-    cmp     r1, r2
-    bcc.n   .loop
-    bx      lr
-
-    .size   gc_helper_get_regs_and_clean_stack, .-gc_helper_get_regs_and_clean_stack
diff --git a/teensy/lcd.c b/teensy/lcd.c
new file mode 100644
index 0000000000000000000000000000000000000000..cc3f52bad5d7379ecb87994560b6e57dd521851c
--- /dev/null
+++ b/teensy/lcd.c
@@ -0,0 +1,14 @@
+#include "misc.h"
+#include "../stm/lcd.h"
+
+void lcd_init(void) {
+}
+
+void lcd_print_str(const char *str) {
+    (void)str;
+}
+
+void lcd_print_strn(const char *str, unsigned int len) {
+    (void)str;
+    (void)len;
+}
diff --git a/teensy/led.c b/teensy/led.c
index 1aad2a365e440f37c58b791bca44333add99d69c..0cef354c2732cb044466ea81f7045c5268ab3d0a 100644
--- a/teensy/led.c
+++ b/teensy/led.c
@@ -61,21 +61,17 @@ mp_obj_t led_obj_off(mp_obj_t self_in) {
 static MP_DEFINE_CONST_FUN_OBJ_1(led_obj_on_obj, led_obj_on);
 static MP_DEFINE_CONST_FUN_OBJ_1(led_obj_off_obj, led_obj_off);
 
+static const mp_method_t led_methods[] = {
+    { "on", &led_obj_on_obj },
+    { "off", &led_obj_off_obj },
+    { NULL, NULL },
+};
+
 static const mp_obj_type_t led_obj_type = {
     { &mp_const_type },
     "Led",
-    led_obj_print, // print
-    NULL, // make_new
-    NULL, // call_n
-    NULL, // unary_op
-    NULL, // binary_op
-    NULL, // getiter
-    NULL, // iternext
-    { // method list
-        { "on", &led_obj_on_obj },
-        { "off", &led_obj_off_obj },
-        { NULL, NULL },
-    }
+    .print = led_obj_print,
+    .methods = led_methods,
 };
 
 mp_obj_t pyb_Led(mp_obj_t led_id) {
diff --git a/teensy/lexerfatfs.c b/teensy/lexerfatfs.c
new file mode 100644
index 0000000000000000000000000000000000000000..2ded0756f70d847a38fba88a39c5bc7e978e8c66
--- /dev/null
+++ b/teensy/lexerfatfs.c
@@ -0,0 +1,17 @@
+#include <stdint.h>
+#include <stdio.h>
+
+#include "misc.h"
+#include "lexer.h"
+typedef int FIL;
+#include "../stm/lexerfatfs.h"
+
+mp_lexer_t *mp_lexer_new_from_file(const char *filename, mp_lexer_file_buf_t *fb) {
+  printf("import not implemented\n");
+  return NULL;
+}
+
+mp_lexer_t *mp_import_open_file(qstr mod_name) {
+    printf("import not implemented\n");
+    return NULL;
+}
diff --git a/teensy/main.c b/teensy/main.c
index 3711cd60d6d4b491975e89ecb5758fc5f1328950..4844941e70bee8f8b8093e36037fe4fa4e792dd1 100644
--- a/teensy/main.c
+++ b/teensy/main.c
@@ -8,7 +8,7 @@
 #include "mpconfig.h"
 #include "mpqstr.h"
 #include "lexer.h"
-#include "lexerteensy.h"
+#include "../stm/lexerstr.h"
 #include "parse.h"
 #include "obj.h"
 #include "compile.h"
@@ -378,7 +378,7 @@ void do_repl(void) {
     stdout_tx_str("\r\n");
 }
 
-void main(void) {
+int main(void) {
     pinMode(LED_BUILTIN, OUTPUT);    
     // Wait for host side to get connected
     while (!usb_vcp_is_connected()) {
diff --git a/teensy/main.cpp b/teensy/main.cpp
deleted file mode 100644
index 52ce51cda2189c557c55011322e1ec495209f74f..0000000000000000000000000000000000000000
--- a/teensy/main.cpp
+++ /dev/null
@@ -1,245 +0,0 @@
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "Arduino.h"
-
-extern "C"
-{
-#include "nlr.h"
-#include "misc.h"
-#include "mpconfig.h"
-#include "lexer.h"
-#include "lexerteensy.h"
-#include "parse.h"
-#include "obj.h"
-#include "compile.h"
-#include "runtime0.h"
-#include "runtime.h"
-#include "repl.h"
-#include "usb.h"
-}
-
-#ifdef USE_READLINE
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif
-
-#if 0
-static char *str_join(const char *s1, int sep_char, const char *s2) {
-    int l1 = strlen(s1);
-    int l2 = strlen(s2);
-    char *s = m_new(char, l1 + l2 + 2);
-    memcpy(s, s1, l1);
-    if (sep_char != 0) {
-        s[l1] = sep_char;
-        l1 += 1;
-    }
-    memcpy(s + l1, s2, l2);
-    s[l1 + l2] = 0;
-    return s;
-}
-
-static char *prompt(char *p) {
-#ifdef USE_READLINE
-    char *line = readline(p);
-    if (line) {
-        add_history(line);
-    }
-#else
-    static char buf[256];
-    fputs(p, stdout);
-    char *s = fgets(buf, sizeof(buf), stdin);
-    if (!s) {
-        return NULL;
-    }
-    int l = strlen(buf);
-    if (buf[l - 1] == '\n') {
-        buf[l - 1] = 0;
-    } else {
-        l++;
-    }
-    char *line = m_new(char, l);
-    memcpy(line, buf, l);
-#endif
-    return line;
-}
-#endif
-
-#define READLINE_HIST_SIZE (8)
-
-static const char *readline_hist[READLINE_HIST_SIZE] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
-
-void stdout_tx_str(const char *str) {
-//    usart_tx_str(str);
-    usb_vcp_send_str(str);
-}
-
-static elapsedMillis ledTime;
-static uint8_t ledState;
-
-int readline(vstr_t *line, const char *prompt) {
-    stdout_tx_str(prompt);
-    int len = vstr_len(line);
-    int escape = 0;
-    int hist_num = 0;
-    for (;;) {
-        char c;
-        ledState = 1;
-        ledTime = 0;
-        digitalWrite(LED_BUILTIN, ledState);
-        for (;;) {
-            if (ledTime > 200) {
-                ledState = !ledState;
-                digitalWrite(LED_BUILTIN, ledState);
-                ledTime = 0;
-            }
-            if (usb_vcp_rx_any() != 0) {
-                c = usb_vcp_rx_get();
-                break;
-#if 0
-            } else if (usart_rx_any()) {
-                c = usart_rx_char();
-                break;
-#endif
-            }
-            //delay(1);
-            //if (storage_needs_flush()) {
-            //    storage_flush();
-            //}
-        }
-        if (escape == 0) {
-            if (c == 4 && vstr_len(line) == len) {
-                return 0;
-            } else if (c == '\r') {
-                stdout_tx_str("\r\n");
-                for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) {
-                    readline_hist[i] = readline_hist[i - 1];
-                }
-                readline_hist[0] = strdup(vstr_str(line));
-                return 1;
-            } else if (c == 27) {
-                escape = true;
-            } else if (c == 127) {
-                if (vstr_len(line) > len) {
-                    vstr_cut_tail(line, 1);
-                    stdout_tx_str("\b \b");
-                }
-            } else if (32 <= c && c <= 126) {
-                vstr_add_char(line, c);
-                stdout_tx_str(line->buf + line->len - 1);
-            }
-        } else if (escape == 1) {
-            if (c == '[') {
-                escape = 2;
-            } else {
-                escape = 0;
-            }
-        } else if (escape == 2) {
-            escape = 0;
-            if (c == 'A') {
-                // up arrow
-                if (hist_num < READLINE_HIST_SIZE && readline_hist[hist_num] != NULL) {
-                    // erase line
-                    for (int i = line->len - len; i > 0; i--) {
-                        stdout_tx_str("\b \b");
-                    }
-                    // set line to history
-                    line->len = len;
-                    vstr_add_str(line, readline_hist[hist_num]);
-                    // draw line
-                    stdout_tx_str(readline_hist[hist_num]);
-                    // increase hist num
-                    hist_num += 1;
-                }
-            }
-        } else {
-            escape = 0;
-        }
-        delay(10);
-    }
-}
-
-void setup(void) {
-    pinMode(LED_BUILTIN, OUTPUT);    
-    ledState = 1;
-    digitalWrite(LED_BUILTIN, ledState);
-    ledTime = 0;
-    // Wait for host side to get connected
-    while (!usb_vcp_is_connected()) {
-        if (ledTime > 100) {
-            ledState = !ledState;
-            digitalWrite(LED_BUILTIN, ledState);
-            ledTime = 0;
-        }
-    }
-    digitalWrite(LED_BUILTIN, 0);
-
-    qstr_init();
-    rt_init();
-
-    stdout_tx_str("Micro Python for Teensy 3.1\r\n");
-    stdout_tx_str("Type \"help()\" for more information.\r\n");
-}
-
-void loop(void) {
-    vstr_t line;
-    vstr_init(&line);
-
-    vstr_reset(&line);
-    int ret = readline(&line, ">>> ");
-    if (ret == 0) {
-        // EOF
-        return;
-    }
-
-    if (vstr_len(&line) == 0) {
-        return;
-    }
-
-    if (mp_repl_is_compound_stmt(vstr_str(&line))) {
-        for (;;) {
-            vstr_add_char(&line, '\n');
-            int len = vstr_len(&line);
-            int ret = readline(&line, "... ");
-            if (ret == 0 || vstr_len(&line) == len) {
-                // done entering compound statement
-                break;
-            }
-        }
-    }
-
-    mp_lexer_str_buf_t sb;
-    mp_lexer_t *lex = mp_lexer_new_from_str_len("<stdin>", vstr_str(&line), vstr_len(&line), false, &sb);
-    mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT);
-    mp_lexer_free(lex);
-
-    if (pn != MP_PARSE_NODE_NULL) {
-        mp_obj_t module_fun = mp_compile(pn, true);
-        if (module_fun != mp_const_none) {
-            nlr_buf_t nlr;
-            uint32_t start = micros();
-            if (nlr_push(&nlr) == 0) {
-                rt_call_function_0(module_fun);
-                nlr_pop();
-                // optional timing
-                if (0) {
-                    uint32_t ticks = micros() - start; // TODO implement a function that does this properly
-                    printf("(took %lu us)\n", ticks);
-                }
-            } else {
-                // uncaught exception
-                mp_obj_print((mp_obj_t)nlr.ret_val);
-                printf("\r\n");
-            }
-        }
-    }
-}
-
-// for sqrt
-#include <math.h>
-machine_float_t machine_sqrt(machine_float_t x) {
-    return sqrt(x);
-}
-
diff --git a/teensy/malloc0.c b/teensy/malloc0.c
deleted file mode 100644
index 1a6cb708e881f1eafad9d869e0f8afaf70468fb7..0000000000000000000000000000000000000000
--- a/teensy/malloc0.c
+++ /dev/null
@@ -1,57 +0,0 @@
-#include <stdint.h>
-#include "std.h"
-#include "mpconfig.h"
-#include "gc.h"
-
-#if 0
-static uint32_t mem = 0;
-
-void *malloc(size_t n) {
-    if (mem == 0) {
-        extern uint32_t _heap_start;
-        mem = (uint32_t)&_heap_start; // need to use big ram block so we can execute code from it (is it true that we can't execute from CCM?)
-    }
-    void *ptr = (void*)mem;
-    mem = (mem + n + 3) & (~3);
-    if (mem > 0x20000000 + 0x18000) {
-        void __fatal_error(const char*);
-        __fatal_error("out of memory");
-    }
-    return ptr;
-}
-
-void free(void *ptr) {
-}
-
-void *realloc(void *ptr, size_t n) {
-    return malloc(n);
-}
-
-#endif
-
-void *calloc(size_t sz, size_t n) {
-    char *ptr = malloc(sz * n);
-    for (int i = 0; i < sz * n; i++) {
-        ptr[i] = 0;
-    }
-    return ptr;
-}
-
-void *malloc(size_t n) {
-    void *m = gc_alloc(n);
-    return m;
-}
-
-void free(void *ptr) {
-    gc_free(ptr);
-}
-
-void *realloc(void *ptr, size_t n) {
-    return gc_realloc(ptr, n);
-}
-
-void __assert_func(void) {
-    printf("\nASSERT FAIL!");
-    for (;;) {
-    }
-}
diff --git a/teensy/mpconfigport.h b/teensy/mpconfigport.h
index 4cea332f3977f5c3206d5ee85523c5e8a08684e8..dfa46cc504d408554aa34c8bd5e26d7c664f3df2 100644
--- a/teensy/mpconfigport.h
+++ b/teensy/mpconfigport.h
@@ -2,11 +2,11 @@
 
 // options to control how Micro Python is built
 
-#define MICROPY_ENABLE_FLOAT        (1)
-#define MICROPY_EMIT_CPYTHON        (0)
-#define MICROPY_EMIT_X64            (0)
 #define MICROPY_EMIT_THUMB          (1)
 #define MICROPY_EMIT_INLINE_THUMB   (1)
+#define MICROPY_ENABLE_GC           (1)
+#define MICROPY_ENABLE_REPL_HELPERS (1)
+#define MICROPY_ENABLE_FLOAT        (1)
 
 // type definitions for the specific machine
 
diff --git a/teensy/printf.c b/teensy/printf.c
deleted file mode 100644
index 71b27b821435d62e63bc597dd838cb893ab2e688..0000000000000000000000000000000000000000
--- a/teensy/printf.c
+++ /dev/null
@@ -1,305 +0,0 @@
-#include <stdint.h>
-#include <stdarg.h>
-#include "std.h"
-#include "misc.h"
-//#include "lcd.h"
-//#include "usart.h"
-#include "usb.h"
-
-#define PF_FLAG_LEFT_ADJUST (0x01)
-#define PF_FLAG_SHOW_SIGN   (0x02)
-#define PF_FLAG_SPACE_SIGN  (0x04)
-#define PF_FLAG_NO_TRAILZ   (0x08)
-#define PF_FLAG_ZERO_PAD    (0x10)
-
-// tricky; we compute pad string by: pad_chars + (flags & PF_FLAG_ZERO_PAD)
-#define PF_PAD_SIZE PF_FLAG_ZERO_PAD
-static const char *pad_chars = "                0000000000000000";
-
-typedef struct _pfenv_t {
-    void *data;
-    void (*print_strn)(void *, const char *str, unsigned int len);
-} pfenv_t;
-
-static void print_str_dummy(void *data, const char *str, unsigned int len) {
-}
-
-const pfenv_t pfenv_dummy = {0, print_str_dummy};
-
-static int pfenv_print_strn(const pfenv_t *pfenv, const char *str, unsigned int len, int flags, int width) {
-    int pad = width - len;
-    if (pad > 0 && (flags & PF_FLAG_LEFT_ADJUST) == 0) {
-        while (pad > 0) {
-            int p = pad;
-            if (p > PF_PAD_SIZE)
-                p = PF_PAD_SIZE;
-            pfenv->print_strn(pfenv->data, pad_chars + (flags & PF_FLAG_ZERO_PAD), p);
-            pad -= p;
-        }
-    }
-    pfenv->print_strn(pfenv->data, str, len);
-    while (pad > 0) {
-        int p = pad;
-        if (p > PF_PAD_SIZE)
-            p = PF_PAD_SIZE;
-        pfenv->print_strn(pfenv->data, pad_chars, p);
-        pad -= p;
-    }
-    return len;
-}
-
-// enough room for 32 signed number
-#define INT_BUF_SIZE (12)
-
-static int pfenv_print_int(const pfenv_t *pfenv, unsigned int x, int sgn, int base, int base_char, int flags, int width) {
-    char sign = 0;
-    if (sgn) {
-        if ((int)x < 0) {
-            sign = '-';
-            x = -x;
-        } else if (flags & PF_FLAG_SHOW_SIGN) {
-            sign = '+';
-        } else if (flags & PF_FLAG_SPACE_SIGN) {
-            sign = ' ';
-        }
-    }
-
-    char buf[INT_BUF_SIZE];
-    char *b = buf + INT_BUF_SIZE;
-
-    if (x == 0) {
-        *(--b) = '0';
-    } else {
-        do {
-            int c = x % base;
-            x /= base;
-            if (c >= 10) {
-                c += base_char - 10;
-            } else {
-                c += '0';
-            }
-            *(--b) = c;
-        } while (b > buf && x != 0);
-    }
-
-    if (b > buf && sign != 0) {
-        *(--b) = sign;
-    }
-
-    return pfenv_print_strn(pfenv, b, buf + INT_BUF_SIZE - b, flags, width);
-}
-
-void pfenv_prints(const pfenv_t *pfenv, const char *str) {
-    pfenv->print_strn(pfenv->data, str, strlen(str));
-}
-
-int pfenv_printf(const pfenv_t *pfenv, const char *fmt, va_list args) {
-    int chrs = 0;
-    for (;;) {
-        {
-            const char *f = fmt;
-            while (*f != '\0' && *f != '%') {
-                ++f; // XXX UTF8 advance char
-            }
-            if (f > fmt) {
-                pfenv->print_strn(pfenv->data, fmt, f - fmt);
-                chrs += f - fmt;
-                fmt = f;
-            }
-        }
-
-        if (*fmt == '\0') {
-            break;
-        }
-
-        // move past % character
-        ++fmt;
-
-        // parse flags, if they exist
-        int flags = 0;
-        while (*fmt != '\0') {
-            if (*fmt == '-') flags |= PF_FLAG_LEFT_ADJUST;
-            else if (*fmt == '+') flags |= PF_FLAG_SHOW_SIGN;
-            else if (*fmt == ' ') flags |= PF_FLAG_SPACE_SIGN;
-            else if (*fmt == '!') flags |= PF_FLAG_NO_TRAILZ;
-            else if (*fmt == '0') flags |= PF_FLAG_ZERO_PAD;
-            else break;
-            ++fmt;
-        }
-
-        // parse width, if it exists
-        int width = 0;
-        for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
-            width = width * 10 + *fmt - '0';
-        }
-
-        // parse precision, if it exists
-        int prec = -1;
-        if (*fmt == '.') {
-            ++fmt;
-            if (*fmt == '*') {
-                ++fmt;
-                prec = va_arg(args, int);
-            } else {
-                prec = 0;
-                for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
-                    prec = prec * 10 + *fmt - '0';
-                }
-            }
-            if (prec < 0) {
-                prec = 0;
-            }
-        }
-
-        // parse long specifiers (current not used)
-        //bool long_arg = false;
-        if (*fmt == 'l') {
-            ++fmt;
-            //long_arg = true;
-        }
-
-        if (*fmt == '\0') {
-            break;
-        }
-
-        switch (*fmt) {
-            case 'b':
-                if (va_arg(args, int)) {
-                    chrs += pfenv_print_strn(pfenv, "true", 4, flags, width);
-                } else {
-                    chrs += pfenv_print_strn(pfenv, "false", 5, flags, width);
-                }
-                break;
-            case 'c':
-            {
-                char str = va_arg(args, int);
-                chrs += pfenv_print_strn(pfenv, &str, 1, flags, width);
-                break;
-            }
-            case 's':
-            {
-                const char *str = va_arg(args, const char*);
-                if (str) {
-                    if (prec < 0) {
-                        prec = strlen(str);
-                    }
-                    chrs += pfenv_print_strn(pfenv, str, prec, flags, width);
-                } else {
-                    chrs += pfenv_print_strn(pfenv, "(null)", 6, flags, width);
-                }
-                break;
-            }
-            case 'u':
-                chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 10, 'a', flags, width);
-                break;
-            case 'd':
-                chrs += pfenv_print_int(pfenv, va_arg(args, int), 1, 10, 'a', flags, width);
-                break;
-            case 'x':
-            case 'p': // ?
-                chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'a', flags, width);
-                break;
-            case 'X':
-            case 'P': // ?
-                chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'A', flags, width);
-                break;
-            default:
-                pfenv->print_strn(pfenv->data, fmt, 1);
-                chrs += 1;
-                break;
-        }
-        ++fmt;
-    }
-    return chrs;
-}
-
-void stdout_print_strn(void *data, const char *str, unsigned int len) {
-    // send stdout to USART, USB CDC VCP, and LCD if nothing else
-#if 0
-    int any = 0;
-    if (usart_is_enabled()) {
-        usart_tx_strn_cooked(str, len);
-        any = true;
-    }
-#endif
-    if (usb_vcp_is_enabled()) {
-        usb_vcp_send_strn_cooked(str, len);
-//        any = true;
-    }
-#if 0
-    if (!any) {
-        lcd_print_strn(str, len);
-    }
-#endif
-}
-
-static const pfenv_t pfenv_stdout = {0, stdout_print_strn};
-
-int printf(const char *fmt, ...) {
-    va_list ap;
-    va_start(ap, fmt);
-    int ret = pfenv_printf(&pfenv_stdout, fmt, ap);
-    va_end(ap);
-    return ret;
-}
-
-int vprintf(const char *fmt, va_list ap) {
-    return pfenv_printf(&pfenv_stdout, fmt, ap);
-}
-
-// need this because gcc optimises printf("%c", c) -> putchar(c), and printf("a") -> putchar('a')
-int putchar(int c) {
-    char chr = c;
-    stdout_print_strn(0, &chr, 1);
-    return chr;
-}
-
-// need this because gcc optimises printf("string\n") -> puts("string")
-int puts(const char *s) {
-    stdout_print_strn(0, s, strlen(s));
-    char chr = '\n';
-    stdout_print_strn(0, &chr, 1);
-    return 1;
-}
-
-typedef struct _strn_pfenv_t {
-    char *cur;
-    size_t remain;
-} strn_pfenv_t;
-
-void strn_print_strn(void *data, const char *str, unsigned int len) {
-    strn_pfenv_t *strn_pfenv = data;
-    if (len > strn_pfenv->remain) {
-        len = strn_pfenv->remain;
-    }
-    memcpy(strn_pfenv->cur, str, len);
-    strn_pfenv->cur += len;
-    strn_pfenv->remain -= len;
-}
-
-int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) {
-    strn_pfenv_t strn_pfenv;
-    strn_pfenv.cur = str;
-    strn_pfenv.remain = size;
-    pfenv_t pfenv;
-    pfenv.data = &strn_pfenv;
-    pfenv.print_strn = strn_print_strn;
-    int len = pfenv_printf(&pfenv, fmt, ap);
-    // add terminating null byte
-    if (size > 0) {
-        if (strn_pfenv.remain == 0) {
-            strn_pfenv.cur[-1] = 0;
-        } else {
-            strn_pfenv.cur[0] = 0;
-        }
-    }
-    return len;
-}
-
-int snprintf(char *str, size_t size, const char *fmt, ...) {
-    va_list ap;
-    va_start(ap, fmt);
-    int ret = vsnprintf(str, size, fmt, ap);
-    va_end(ap);
-    return ret;
-}
diff --git a/teensy/string0.c b/teensy/string0.c
deleted file mode 100644
index 2a5f255971bca2e833cff87251605673c65592d0..0000000000000000000000000000000000000000
--- a/teensy/string0.c
+++ /dev/null
@@ -1,110 +0,0 @@
-#include <stdint.h>
-#include "std.h"
-
-void *memcpy(void *dest, const void *src, size_t n) {
-    // TODO align and copy 32 bits at a time
-    uint8_t *d = dest;
-    const uint8_t *s = src;
-    for (; n > 0; n--) {
-        *d++ = *s++;
-    }
-    return dest;
-}
-
-void *memmove(void *dest, const void *src, size_t n) {
-    if (src < dest && dest < src + n) {
-        // need to copy backwards
-        uint8_t *d = dest + n - 1;
-        const uint8_t *s = src + n - 1;
-        for (; n > 0; n--) {
-            *d-- = *s--;
-        }
-        return dest;
-    } else {
-        // can use normal memcpy
-        return memcpy(dest, src, n);
-    }
-}
-
-void *memset(void *s, int c, size_t n) {
-    uint8_t *s2 = s;
-    for (; n > 0; n--) {
-        *s2++ = c;
-    }
-    return s;
-}
-
-int strlen(const char *str) {
-    int len = 0;
-    for (const char *s = str; *s; s++) {
-        len += 1;
-    }
-    return len;
-}
-
-int strcmp(const char *s1, const char *s2) {
-    while (*s1 && *s2) {
-        char c1 = *s1++; // XXX UTF8 get char, next char
-        char c2 = *s2++; // XXX UTF8 get char, next char
-        if (c1 < c2) return -1;
-        else if (c1 > c2) return 1;
-    }
-    if (*s2) return -1;
-    else if (*s1) return 1;
-    else return 0;
-}
-
-int strncmp(const char *s1, const char *s2, size_t n) {
-    while (*s1 && *s2 && n > 0) {
-        char c1 = *s1++; // XXX UTF8 get char, next char
-        char c2 = *s2++; // XXX UTF8 get char, next char
-        n--;
-        if (c1 < c2) return -1;
-        else if (c1 > c2) return 1;
-    }
-    if (n == 0) return 0;
-    else if (*s2) return -1;
-    else if (*s1) return 1;
-    else return 0;
-}
-
-char *strndup(const char *s, size_t n) {
-    size_t len = strlen(s);
-    if (n > len) {
-        n = len;
-    }
-    char *s2 = malloc(n + 1);
-    memcpy(s2, s, n);
-    s2[n] = '\0';
-    return s2;
-}
-
-char *strcpy(char *dest, const char *src) {
-    char *d = dest;
-    while (*src) {
-        *d++ = *src++;
-    }
-    *d = '\0';
-    return dest;
-}
-
-// needed because gcc optimises strcpy + strcat to this
-char *stpcpy(char *dest, const char *src) {
-    while (*src) {
-        *dest++ = *src++;
-    }
-    *dest = '\0';
-    return dest;
-}
-
-char *strcat(char *dest, const char *src) {
-    char *d = dest;
-    while (*d) {
-        d++;
-    }
-    while (*src) {
-        *d++ = *src++;
-    }
-    *d = '\0';
-    return dest;
-}
diff --git a/teensy/usart.c b/teensy/usart.c
new file mode 100644
index 0000000000000000000000000000000000000000..419dac9527d69dda25a0140c72d9a2104129d59f
--- /dev/null
+++ b/teensy/usart.c
@@ -0,0 +1,30 @@
+#include "misc.h"
+#include "../stm/usart.h"
+
+void usart_init(void) {
+}
+
+bool usart_is_enabled(void) {
+    return false;
+}
+
+bool usart_rx_any(void) {
+    return false;
+}
+
+int usart_rx_char(void) {
+    return 0;
+}
+
+void usart_tx_char(int c) {
+    (void)c;
+}
+
+void usart_tx_str(const char *str) {
+    (void)str;
+}
+
+void usart_tx_strn_cooked(const char *str, int len) {
+    (void)str;
+    (void)len;
+}