From 4f1b7fec9f103c92de40875e9a06b7decc4923f4 Mon Sep 17 00:00:00 2001
From: Dave Hylands <dhylands@gmail.com>
Date: Sun, 15 Jun 2014 22:33:14 -0700
Subject: [PATCH] Updated teensy to build.

Refactored some stmhal files which are shared with teensy.
---
 py/mkrules.mk                         |   6 +
 stmhal/gccollect.c                    |   5 +-
 stmhal/mpconfigport.h                 |   8 +
 stmhal/pin.c                          |  13 +-
 stmhal/pin.h                          |  80 +---
 stmhal/pin_defs_stmhal.h              |  96 +++++
 stmhal/pin_named_pins.c               |   5 +-
 stmhal/pybstdio.c                     |   5 +-
 stmhal/pyexec.c                       |   5 +-
 stmhal/readline.c                     |   5 +-
 teensy/Makefile                       |  59 ++-
 teensy/hal_gpio.c                     | 115 ++++++
 teensy/help.c                         | 123 ++++++
 teensy/import.c                       |  22 ++
 teensy/lcd.c                          |   7 +-
 teensy/led.c                          | 153 +++++---
 teensy/led.h                          |   4 +-
 teensy/lexerfatfs.c                   |  11 +-
 teensy/lexermemzip.c                  |   2 +-
 teensy/main.c                         | 411 ++++++--------------
 teensy/make-pins.py                   | 326 ++++++++++++++++
 teensy/memzip.c                       |  88 ++++-
 teensy/memzip.h                       |  10 +
 teensy/memzip_files/boot.py           |   1 +
 teensy/memzip_files/{src => }/main.py |  10 +-
 teensy/memzip_files/src/test.py       |   1 -
 teensy/memzip_files/test.py           |   1 -
 teensy/mk20dx256-af.csv               |  65 ++++
 teensy/mk20dx256-prefix.c             |  37 ++
 teensy/mk20dx256.ld                   | 174 ++++-----
 teensy/modpyb.c                       | 341 +++++++++++++++++
 teensy/mpconfigport.h                 |  88 ++++-
 teensy/pin_defs_teensy.h              |  46 +++
 teensy/qstrdefsport.h                 |  62 ++-
 teensy/servo.h                        |   7 +-
 teensy/teensy-pins.csv                |  55 +++
 teensy/teensy_hal.c                   |  16 +
 teensy/teensy_hal.h                   | 108 ++++++
 teensy/uart.c                         | 524 ++++++++++++++++++++++++++
 teensy/usart.c                        |  38 --
 teensy/usb.c                          |   9 +-
 41 files changed, 2541 insertions(+), 601 deletions(-)
 create mode 100644 stmhal/pin_defs_stmhal.h
 create mode 100644 teensy/hal_gpio.c
 create mode 100644 teensy/help.c
 create mode 100644 teensy/import.c
 create mode 100755 teensy/make-pins.py
 rename teensy/memzip_files/{src => }/main.py (57%)
 delete mode 100644 teensy/memzip_files/src/test.py
 delete mode 100644 teensy/memzip_files/test.py
 create mode 100644 teensy/mk20dx256-af.csv
 create mode 100644 teensy/mk20dx256-prefix.c
 create mode 100644 teensy/modpyb.c
 create mode 100644 teensy/pin_defs_teensy.h
 create mode 100644 teensy/teensy-pins.csv
 create mode 100644 teensy/teensy_hal.c
 create mode 100644 teensy/teensy_hal.h
 create mode 100644 teensy/uart.c
 delete mode 100644 teensy/usart.c

diff --git a/py/mkrules.mk b/py/mkrules.mk
index 9592d6c590..7d366c9c3e 100644
--- a/py/mkrules.mk
+++ b/py/mkrules.mk
@@ -97,4 +97,10 @@ print-cfg:
 	$(ECHO) "OBJ    = $(OBJ)"
 .PHONY: print-cfg
 
+print-def:
+	@$(ECHO) "The following defines are built into the $(CC) compiler"
+	touch __empty__.c
+	@$(CC) -E -Wp,-dM __empty__.c
+	@$(RM) -f __empty__.c
+
 -include $(OBJ:.o=.P)
diff --git a/stmhal/gccollect.c b/stmhal/gccollect.c
index 79082e2f2e..64ac7baa7d 100644
--- a/stmhal/gccollect.c
+++ b/stmhal/gccollect.c
@@ -25,8 +25,7 @@
  */
 
 #include <stdio.h>
-
-#include <stm32f4xx_hal.h>
+#include <stdint.h>
 
 #include "misc.h"
 #include "mpconfig.h"
@@ -35,6 +34,8 @@
 #include "gc.h"
 #include "gccollect.h"
 
+#include HAL_H
+
 machine_uint_t gc_helper_get_regs_and_sp(machine_uint_t *regs);
 
 // obsolete
diff --git a/stmhal/mpconfigport.h b/stmhal/mpconfigport.h
index 28cd90bb01..7c1f582755 100644
--- a/stmhal/mpconfigport.h
+++ b/stmhal/mpconfigport.h
@@ -104,3 +104,11 @@ typedef const void *machine_const_ptr_t; // must be of pointer size
 
 // We need to provide a declaration/definition of alloca()
 #include <alloca.h>
+
+#define HAL_H           <stm32f4xx_hal.h>
+#define PIN_DEFS_PORT_H "pin_defs_stmhal.h"
+
+#define GPIO_read_pin(gpio, pin)        (((gpio)->IDR >> (pin)) & 1)
+#define GPIO_set_pin(gpio, pin_mask)    (((gpio)->BSRRL) = (pin_mask))
+#define GPIO_clear_pin(gpio, pin_mask)  (((gpio)->BSRRH) = (pin_mask))
+
diff --git a/stmhal/pin.c b/stmhal/pin.c
index 9806a8c49a..6ee5fa564e 100644
--- a/stmhal/pin.c
+++ b/stmhal/pin.c
@@ -28,9 +28,8 @@
 #include <stdint.h>
 #include <string.h>
 
-#include "stm32f4xx_hal.h"
-
 #include "mpconfig.h"
+#include HAL_H
 #include "nlr.h"
 #include "misc.h"
 #include "qstr.h"
@@ -310,13 +309,13 @@ STATIC mp_obj_t pin_value(uint n_args, mp_obj_t *args) {
     pin_obj_t *self = args[0];
     if (n_args == 1) {
         // get pin
-        return MP_OBJ_NEW_SMALL_INT((self->gpio->IDR >> self->pin) & 1);
+        return MP_OBJ_NEW_SMALL_INT(GPIO_read_pin(self->gpio, self->pin));
     } else {
         // set pin
         if (mp_obj_is_true(args[1])) {
-            self->gpio->BSRRL = self->pin_mask;
+            GPIO_set_pin(self->gpio, self->pin_mask);
         } else {
-            self->gpio->BSRRH = self->pin_mask;
+            GPIO_clear_pin(self->gpio, self->pin_mask);
         }
         return mp_const_none;
     }
@@ -327,7 +326,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pin_value_obj, 1, 2, pin_value);
 /// Set the pin to a low logic level.
 STATIC mp_obj_t pin_low(mp_obj_t self_in) {
     pin_obj_t *self = self_in;
-    self->gpio->BSRRH = self->pin_mask;
+    GPIO_clear_pin(self->gpio, self->pin_mask);;
     return mp_const_none;
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_low_obj, pin_low);
@@ -336,7 +335,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_low_obj, pin_low);
 /// Set the pin to a high logic level.
 STATIC mp_obj_t pin_high(mp_obj_t self_in) {
     pin_obj_t *self = self_in;
-    self->gpio->BSRRL = self->pin_mask;
+    GPIO_set_pin(self->gpio, self->pin_mask);;
     return mp_const_none;
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_high_obj, pin_high);
diff --git a/stmhal/pin.h b/stmhal/pin.h
index d60bd22fd7..8b3b862773 100644
--- a/stmhal/pin.h
+++ b/stmhal/pin.h
@@ -24,63 +24,10 @@
  * THE SOFTWARE.
  */
 
-enum {
-  PORT_A,
-  PORT_B,
-  PORT_C,
-  PORT_D,
-  PORT_E,
-  PORT_F,
-  PORT_G,
-  PORT_H,
-  PORT_I,
-  PORT_J,
-};
+// This file requires pin_defs_xxx.h (which has port specific enums and
+// defines, so we include it here. It should never be included directly
 
-enum {
-  AF_FN_TIM,
-  AF_FN_I2C,
-  AF_FN_USART,
-  AF_FN_UART = AF_FN_USART,
-  AF_FN_SPI
-};
-
-enum {
-  AF_PIN_TYPE_TIM_CH1 = 0,
-  AF_PIN_TYPE_TIM_CH2,
-  AF_PIN_TYPE_TIM_CH3,
-  AF_PIN_TYPE_TIM_CH4,
-  AF_PIN_TYPE_TIM_CH1N,
-  AF_PIN_TYPE_TIM_CH2N,
-  AF_PIN_TYPE_TIM_CH3N,
-  AF_PIN_TYPE_TIM_CH1_ETR,
-  AF_PIN_TYPE_TIM_ETR,
-  AF_PIN_TYPE_TIM_BKIN,
-
-  AF_PIN_TYPE_I2C_SDA = 0,
-  AF_PIN_TYPE_I2C_SCL,
-
-  AF_PIN_TYPE_USART_TX = 0,
-  AF_PIN_TYPE_USART_RX,
-  AF_PIN_TYPE_USART_CTS,
-  AF_PIN_TYPE_USART_RTS,
-  AF_PIN_TYPE_USART_CK,
-  AF_PIN_TYPE_UART_TX  = AF_PIN_TYPE_USART_TX,
-  AF_PIN_TYPE_UART_RX  = AF_PIN_TYPE_USART_RX,
-  AF_PIN_TYPE_UART_CTS = AF_PIN_TYPE_USART_CTS,
-  AF_PIN_TYPE_UART_RTS = AF_PIN_TYPE_USART_RTS,
-
-  AF_PIN_TYPE_SPI_MOSI = 0,
-  AF_PIN_TYPE_SPI_MISO,
-  AF_PIN_TYPE_SPI_SCK,
-  AF_PIN_TYPE_SPI_NSS,
-};
-
-enum {
-  PIN_ADC1  = (1 << 0),
-  PIN_ADC2  = (1 << 1),
-  PIN_ADC3  = (1 << 2),
-};
+#include PIN_DEFS_PORT_H
 
 typedef struct {
   mp_obj_base_t base;
@@ -91,24 +38,21 @@ typedef struct {
 
   union {
     void          *reg;
-    TIM_TypeDef   *TIM;
-    I2C_TypeDef   *I2C;
-    USART_TypeDef *USART;
-    USART_TypeDef *UART;
-    SPI_TypeDef   *SPI;
+
+    PIN_DEFS_PORT_AF_UNION
   };
 } pin_af_obj_t;
 
 typedef struct {
   mp_obj_base_t base;
   const char *name;
-  uint16_t port   : 4;
-  uint16_t pin    : 4;
-  uint16_t num_af : 4;
-  uint16_t adc_channel : 4;
-  uint16_t adc_num  : 3;  // 1 bit per ADC
-  uint16_t pin_mask;
-  GPIO_TypeDef *gpio;
+  uint32_t port   : 4;
+  uint32_t pin    : 5;      // Some ARM processors use 32 bits/PORT
+  uint32_t num_af : 4;
+  uint32_t adc_channel : 5; // Some ARM processors use 32 bits/PORT
+  uint32_t adc_num  : 3;    // 1 bit per ADC
+  uint32_t pin_mask;
+  pin_gpio_t *gpio;
   const pin_af_obj_t *af;
 } pin_obj_t;
 
diff --git a/stmhal/pin_defs_stmhal.h b/stmhal/pin_defs_stmhal.h
new file mode 100644
index 0000000000..2b6d9da0e7
--- /dev/null
+++ b/stmhal/pin_defs_stmhal.h
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 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.
+ */
+
+// This file contains pin definitions that are specific to the stmhal port.
+// This file should only ever be #included by pin.h and not directly.
+
+enum {
+  PORT_A,
+  PORT_B,
+  PORT_C,
+  PORT_D,
+  PORT_E,
+  PORT_F,
+  PORT_G,
+  PORT_H,
+  PORT_I,
+  PORT_J,
+};
+
+enum {
+  AF_FN_TIM,
+  AF_FN_I2C,
+  AF_FN_USART,
+  AF_FN_UART = AF_FN_USART,
+  AF_FN_SPI
+};
+
+enum {
+  AF_PIN_TYPE_TIM_CH1 = 0,
+  AF_PIN_TYPE_TIM_CH2,
+  AF_PIN_TYPE_TIM_CH3,
+  AF_PIN_TYPE_TIM_CH4,
+  AF_PIN_TYPE_TIM_CH1N,
+  AF_PIN_TYPE_TIM_CH2N,
+  AF_PIN_TYPE_TIM_CH3N,
+  AF_PIN_TYPE_TIM_CH1_ETR,
+  AF_PIN_TYPE_TIM_ETR,
+  AF_PIN_TYPE_TIM_BKIN,
+
+  AF_PIN_TYPE_I2C_SDA = 0,
+  AF_PIN_TYPE_I2C_SCL,
+
+  AF_PIN_TYPE_USART_TX = 0,
+  AF_PIN_TYPE_USART_RX,
+  AF_PIN_TYPE_USART_CTS,
+  AF_PIN_TYPE_USART_RTS,
+  AF_PIN_TYPE_USART_CK,
+  AF_PIN_TYPE_UART_TX  = AF_PIN_TYPE_USART_TX,
+  AF_PIN_TYPE_UART_RX  = AF_PIN_TYPE_USART_RX,
+  AF_PIN_TYPE_UART_CTS = AF_PIN_TYPE_USART_CTS,
+  AF_PIN_TYPE_UART_RTS = AF_PIN_TYPE_USART_RTS,
+
+  AF_PIN_TYPE_SPI_MOSI = 0,
+  AF_PIN_TYPE_SPI_MISO,
+  AF_PIN_TYPE_SPI_SCK,
+  AF_PIN_TYPE_SPI_NSS,
+};
+
+enum {
+  PIN_ADC1  = (1 << 0),
+  PIN_ADC2  = (1 << 1),
+  PIN_ADC3  = (1 << 2),
+};
+
+#define PIN_DEFS_PORT_AF_UNION \
+    TIM_TypeDef   *TIM; \
+    I2C_TypeDef   *I2C; \
+    USART_TypeDef *USART; \
+    USART_TypeDef *UART; \
+    SPI_TypeDef   *SPI;
+
+typedef GPIO_TypeDef pin_gpio_t;
+
diff --git a/stmhal/pin_named_pins.c b/stmhal/pin_named_pins.c
index 3a1794e1d9..137b6bef82 100644
--- a/stmhal/pin_named_pins.c
+++ b/stmhal/pin_named_pins.c
@@ -28,10 +28,11 @@
 #include <stdint.h>
 #include <string.h>
 
-#include "stm32f4xx_hal.h"
-
 #include "misc.h"
 #include "mpconfig.h"
+
+#include HAL_H
+
 #include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
diff --git a/stmhal/pybstdio.c b/stmhal/pybstdio.c
index 05ea06eb44..b6b5560be1 100644
--- a/stmhal/pybstdio.c
+++ b/stmhal/pybstdio.c
@@ -25,8 +25,7 @@
  */
 
 #include <stdio.h>
-
-#include <stm32f4xx_hal.h>
+#include <stdint.h>
 
 #include "misc.h"
 #include "mpconfig.h"
@@ -38,6 +37,8 @@
 #include "usb.h"
 #include "uart.h"
 
+#include HAL_H
+
 // TODO make stdin, stdout and stderr writable objects so they can
 // be changed by Python code.
 
diff --git a/stmhal/pyexec.c b/stmhal/pyexec.c
index 45928427e1..48be225af2 100644
--- a/stmhal/pyexec.c
+++ b/stmhal/pyexec.c
@@ -26,8 +26,7 @@
 
 #include <stdlib.h>
 #include <stdio.h>
-
-#include <stm32f4xx_hal.h>
+#include <stdint.h>
 
 #include "mpconfig.h"
 #include "nlr.h"
@@ -50,6 +49,8 @@
 #include "usb.h"
 #include "genhdr/py-version.h"
 
+#include HAL_H
+
 pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
 STATIC bool repl_display_debugging_info = 0;
 
diff --git a/stmhal/readline.c b/stmhal/readline.c
index d40bd4219b..f75734396d 100644
--- a/stmhal/readline.c
+++ b/stmhal/readline.c
@@ -25,10 +25,9 @@
  */
 
 #include <stdio.h>
+#include <stdint.h>
 #include <string.h>
 
-#include <stm32f4xx_hal.h>
-
 #include "misc.h"
 #include "mpconfig.h"
 #include "qstr.h"
@@ -38,6 +37,8 @@
 #include "readline.h"
 #include "usb.h"
 
+#include HAL_H
+
 #if 0 // print debugging info
 #define DEBUG_PRINT (1)
 #define DEBUG_printf printf
diff --git a/teensy/Makefile b/teensy/Makefile
index 00f3514724..76cc81ee19 100644
--- a/teensy/Makefile
+++ b/teensy/Makefile
@@ -20,12 +20,15 @@ CFLAGS_CORTEX_M4 = -mthumb -mtune=cortex-m4 -mcpu=cortex-m4 -fsingle-precision-c
 
 INC =  -I.
 INC += -I$(PY_SRC)
+INC += -I../stmhal
 INC += -I$(BUILD)
 INC += -I$(CORE_PATH)
 
 CFLAGS = $(INC) -Wall -ansi -std=gnu99 $(CFLAGS_CORTEX_M4)
 LDFLAGS = -nostdlib -T mk20dx256.ld
-LIBS = -L $(COMPILER_PATH)/../lib/gcc/arm-none-eabi/4.7.2/thumb2 -lgcc
+LIBS  = -L $(COMPILER_PATH)/../arm-none-eabi/lib/thumb2 -lm
+LIBS += -L $(COMPILER_PATH)/../arm-none-eabi/lib/thumb2 -lc
+LIBS += -L $(COMPILER_PATH)/../lib/gcc/arm-none-eabi/4.7.2/thumb2 -lgcc
 
 #Debugging/Optimization
 ifdef DEBUG
@@ -35,23 +38,32 @@ CFLAGS += -Os #-DNDEBUG
 endif
 
 SRC_C = \
+	hal_gpio.c \
+	help.c \
+	import.c \
 	main.c \
 	lcd.c \
 	led.c \
-	lexerfatfs.c \
 	lexermemzip.c \
 	memzip.c \
-	servo.c \
-	usart.c \
+	modpyb.c \
+	teensy_hal.c \
+	uart.c \
 	usb.c \
 
-STM_SRC_C = $(addprefix stm/,\
-	malloc0.c \
+STM_SRC_C = $(addprefix stmhal/,\
+	gccollect.c \
+	input.c \
+	pin.c \
+	pin_named_pins.c \
 	printf.c \
+	pyexec.c \
+	pybstdio.c \
+	readline.c \
 	string0.c \
 	)
 
-STM_SRC_S = $(addprefix stm/,\
+STM_SRC_S = $(addprefix stmhal/,\
 	gchelper.s \
 	)
 
@@ -66,9 +78,7 @@ SRC_TEENSY = \
 	yield.c \
 
 OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(STM_SRC_C:.c=.o) $(STM_SRC_S:.s=.o) $(SRC_TEENSY:.c=.o))
-#LIB = -lreadline
-# the following is needed for BSD
-#LIB += -ltermcap
+OBJ += $(BUILD)/pins_gen.o
 
 all: hex
 hex: $(BUILD)/micropython-mz.hex
@@ -84,7 +94,7 @@ reboot:
 upload: post_compile reboot
 
 $(BUILD)/micropython.elf: $(OBJ)
-	$(ECHO) "LINK $<"
+	$(ECHO) "LINK $@"
 	$(Q)$(CC) $(LDFLAGS) -o "$@" -Wl,-Map,$(@:.elf=.map) $(OBJ) $(LIBS)
 	$(Q)$(SIZE) $@
 
@@ -103,4 +113,31 @@ $(BUILD)/%.hex: $(BUILD)/%.elf
 $(BUILD)/%.o: $(CORE_PATH)/%.c
 	$(call compile_c)
 
+MAKE_PINS = make-pins.py
+BOARD_PINS = teensy-pins.csv
+AF_FILE = mk20dx256-af.csv
+PREFIX_FILE = mk20dx256-prefix.c
+GEN_PINS_SRC = $(BUILD)/pins_gen.c
+GEN_PINS_HDR = $(HEADER_BUILD)/pins.h
+
+# Making OBJ use an order-only depenedency on the generated pins.h file
+# has the side effect of making the pins.h file before we actually compile
+# any of the objects. The normal dependency generation will deal with the
+# case when pins.h is modified. But when it doesn't exist, we don't know
+# which source files might need it.
+$(OBJ): | $(HEADER_BUILD)/pins.h
+
+# Use a pattern rule here so that make will only call make-pins.py once to make
+# both pins_$(BOARD).c and pins.h
+$(BUILD)/%_gen.c $(HEADER_BUILD)/%.h: teensy-%.csv $(MAKE_PINS) $(AF_FILE) $(PREFIX_FILE)
+	$(ECHO) "Create $@"
+	$(Q)$(PYTHON) $(MAKE_PINS) --board $(BOARD_PINS) --af $(AF_FILE) --prefix $(PREFIX_FILE) --hdr $(GEN_PINS_HDR) > $(GEN_PINS_SRC)
+
+$(BUILD)/pins_gen.o: $(BUILD)/pins_gen.c
+	$(call compile_c)
+
+$(BUILD)/%.pp: $(BUILD)/%.c
+	$(ECHO) "PreProcess $<"
+	$(Q)$(CC) $(CFLAGS) -E -Wp,-C,-dD,-dI -o $@ $<
+
 include ../py/mkrules.mk
diff --git a/teensy/hal_gpio.c b/teensy/hal_gpio.c
new file mode 100644
index 0000000000..0811f76fda
--- /dev/null
+++ b/teensy/hal_gpio.c
@@ -0,0 +1,115 @@
+#include <stdint.h>
+#include <mk20dx128.h>
+#include "teensy_hal.h"
+
+#define GPIO_NUMBER 32
+
+void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
+{
+    /* Check the parameters */
+    assert_param(IS_GPIO_PIN(GPIO_Init->Pin));
+    assert_param(IS_GPIO_MODE(GPIO_Init->Mode));
+    assert_param(IS_GPIO_PULL(GPIO_Init->Pull));
+
+    /* Configure the port pins */
+    for (uint32_t position = 0; position < GPIO_NUMBER; position++) {
+        uint32_t bitmask = 1 << position;
+        if ((GPIO_Init->Pin & bitmask) == 0) {
+            continue;
+        }
+        
+        volatile uint32_t *port_pcr = GPIO_PIN_TO_PORT_PCR(GPIOx, position);
+
+        /*--------------------- GPIO Mode Configuration ------------------------*/
+        /* In case of Alternate function mode selection */
+        if ((GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_OD)) {
+            /* Check the Alternate function parameter */
+            assert_param(IS_GPIO_AF(GPIO_Init->Alternate));
+            /* Configure Alternate function mapped with the current IO */
+
+            *port_pcr &= ~PORT_PCR_MUX_MASK;
+            *port_pcr |= PORT_PCR_MUX(GPIO_Init->Alternate);
+        }
+
+        /* Configure IO Direction mode (Input, Output, Alternate or Analog) */
+        if (GPIO_Init->Mode == GPIO_MODE_INPUT || GPIO_Init->Mode == GPIO_MODE_ANALOG) {
+            GPIOx->PDDR &= ~bitmask;
+        } else {
+            GPIOx->PDDR |= bitmask;
+        }
+
+        /* In case of Output or Alternate function mode selection */
+        if ((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) ||
+            (GPIO_Init->Mode == GPIO_MODE_OUTPUT_OD) || (GPIO_Init->Mode == GPIO_MODE_AF_OD)) {
+            /* Check the Speed parameter */
+            assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));
+
+            /* Configure the IO Speed */
+            if (GPIO_Init->Speed > GPIO_SPEED_MEDIUM) {
+                *port_pcr &= ~PORT_PCR_SRE;
+            } else {
+                *port_pcr |= PORT_PCR_SRE;
+            }
+
+            /* Configure the IO Output Type */
+            if (GPIO_Init->Mode & GPIO_OUTPUT_TYPE) {
+                *port_pcr |= PORT_PCR_ODE;
+            } else {
+                *port_pcr &= ~PORT_PCR_ODE;
+            }
+        }
+
+        /* Activate the Pull-up or Pull down resistor for the current IO */
+        if (GPIO_Init->Pull == GPIO_NOPULL) {
+            *port_pcr &= ~PORT_PCR_PE;
+        } else {
+            *port_pcr |= PORT_PCR_PE;
+            if (GPIO_Init->Pull == GPIO_PULLDOWN) {
+                *port_pcr &= ~PORT_PCR_PS;
+            } else {
+                *port_pcr |= PORT_PCR_PS;
+            }
+        }
+
+#if 0
+        /*--------------------- EXTI Mode Configuration ------------------------*/
+        /* Configure the External Interrupt or event for the current IO */
+        if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE)
+        {
+          /* Enable SYSCFG Clock */
+          __SYSCFG_CLK_ENABLE();
+
+          temp = ((uint32_t)0x0F) << (4 * (position & 0x03));
+          SYSCFG->EXTICR[position >> 2] &= ~temp;
+          SYSCFG->EXTICR[position >> 2] |= ((uint32_t)(__HAL_GET_GPIO_SOURCE(GPIOx)) << (4 * (position & 0x03)));
+
+          /* Clear EXTI line configuration */
+          EXTI->IMR &= ~((uint32_t)iocurrent);
+          EXTI->EMR &= ~((uint32_t)iocurrent);
+
+          if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT)
+          {
+            EXTI->IMR |= iocurrent;
+          }
+          if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT)
+          {
+            EXTI->EMR |= iocurrent;
+          }
+
+          /* Clear Rising Falling edge configuration */
+          EXTI->RTSR &= ~((uint32_t)iocurrent);
+          EXTI->FTSR &= ~((uint32_t)iocurrent);
+
+          if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE)
+          {
+            EXTI->RTSR |= iocurrent;
+          }
+          if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE)
+          {
+            EXTI->FTSR |= iocurrent;
+          }
+        }
+#endif
+    }
+}
+
diff --git a/teensy/help.c b/teensy/help.c
new file mode 100644
index 0000000000..598b6f4e30
--- /dev/null
+++ b/teensy/help.c
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 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 <stdio.h>
+
+#include "mpconfig.h"
+#include "nlr.h"
+#include "misc.h"
+#include "qstr.h"
+#include "obj.h"
+
+STATIC const char *help_text =
+"Welcome to Micro Python!\n"
+"\n"
+"For online help please visit http://micropython.org/help/.\n"
+"\n"
+"Quick overview of commands for the board:\n"
+"  pyb.info()    -- print some general information\n"
+"  pyb.gc()      -- run the garbage collector\n"
+"  pyb.delay(n)  -- wait for n milliseconds\n"
+"  pyb.Switch()  -- create a switch object\n"
+"                   Switch methods: (), callback(f)\n"
+"  pyb.LED(n)    -- create an LED object for LED n (n=1,2,3,4)\n"
+"                   LED methods: on(), off(), toggle(), intensity(<n>)\n"
+"  pyb.Pin(pin)  -- get a pin, eg pyb.Pin('X1')\n"
+"  pyb.Pin(pin, m, [p]) -- get a pin and configure it for IO mode m, pull mode p\n"
+"                   Pin methods: init(..), value([v]), high(), low()\n"
+"  pyb.ExtInt(pin, m, p, callback) -- create an external interrupt object\n"
+"  pyb.ADC(pin)  -- make an analog object from a pin\n"
+"                   ADC methods: read(), read_timed(buf, freq)\n"
+"  pyb.DAC(port) -- make a DAC object\n"
+"                   DAC methods: triangle(freq), write(n), write_timed(buf, freq)\n"
+"  pyb.RTC()     -- make an RTC object; methods: datetime([val])\n"
+"  pyb.rng()     -- get a 30-bit hardware random number\n"
+"  pyb.Servo(n)  -- create Servo object for servo n (n=1,2,3,4)\n"
+"                   Servo methods: calibration(..), angle([x, [t]]), speed([x, [t]])\n"
+"  pyb.Accel()   -- create an Accelerometer object\n"
+"                   Accelerometer methods: x(), y(), z(), tilt(), filtered_xyz()\n"
+"\n"
+"Pins are numbered X1-X12, X17-X22, Y1-Y12, or by their MCU name\n"
+"Pin IO modes are: pyb.Pin.IN, pyb.Pin.OUT_PP, pyb.Pin.OUT_OD\n"
+"Pin pull modes are: pyb.Pin.PULL_NONE, pyb.Pin.PULL_UP, pyb.Pin.PULL_DOWN\n"
+"Additional serial bus objects: pyb.I2C(n), pyb.SPI(n), pyb.UART(n)\n"
+"\n"
+"Control commands:\n"
+"  CTRL-A        -- on a blank line, enter raw REPL mode\n"
+"  CTRL-B        -- on a blank line, enter normal REPL mode\n"
+"  CTRL-C        -- interrupt a running program\n"
+"  CTRL-D        -- on a blank line, do a soft reset of the board\n"
+"\n"
+"For further help on a specific object, type help(obj)\n"
+;
+
+STATIC void pyb_help_print_info_about_object(mp_obj_t name_o, mp_obj_t value) {
+    printf("  ");
+    mp_obj_print(name_o, PRINT_STR);
+    printf(" -- ");
+    mp_obj_print(value, PRINT_STR);
+    printf("\n");
+}
+
+STATIC mp_obj_t pyb_help(uint n_args, const mp_obj_t *args) {
+    if (n_args == 0) {
+        // print a general help message
+        printf("%s", help_text);
+
+    } else {
+        // try to print something sensible about the given object
+
+        printf("object ");
+        mp_obj_print(args[0], PRINT_STR);
+        printf(" is of type %s\n", mp_obj_get_type_str(args[0]));
+
+        mp_map_t *map = NULL;
+        if (MP_OBJ_IS_TYPE(args[0], &mp_type_module)) {
+            map = mp_obj_dict_get_map(mp_obj_module_get_globals(args[0]));
+        } else {
+            mp_obj_type_t *type;
+            if (MP_OBJ_IS_TYPE(args[0], &mp_type_type)) {
+                type = args[0];
+            } else {
+                type = mp_obj_get_type(args[0]);
+            }
+            if (type->locals_dict != MP_OBJ_NULL && MP_OBJ_IS_TYPE(type->locals_dict, &mp_type_dict)) {
+                map = mp_obj_dict_get_map(type->locals_dict);
+            }
+        }
+        if (map != NULL) {
+            for (uint i = 0; i < map->alloc; i++) {
+                if (map->table[i].key != MP_OBJ_NULL) {
+                    pyb_help_print_info_about_object(map->table[i].key, map->table[i].value);
+                }
+            }
+        }
+    }
+
+    return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_help_obj, 0, 1, pyb_help);
diff --git a/teensy/import.c b/teensy/import.c
new file mode 100644
index 0000000000..967de102ac
--- /dev/null
+++ b/teensy/import.c
@@ -0,0 +1,22 @@
+#include <stdio.h>
+#include <stdint.h>
+
+#include "misc.h"
+#include "mpconfig.h"
+#include "qstr.h"
+#include "lexer.h"
+
+#include "memzip.h"
+
+mp_import_stat_t mp_import_stat(const char *path) {
+    MEMZIP_FILE_INFO info;
+
+    if (memzip_stat(path, &info) != MZ_OK) {
+        return MP_IMPORT_STAT_NO_EXIST;
+    }
+
+    if (info.is_dir) {
+        return MP_IMPORT_STAT_DIR;
+    }
+    return MP_IMPORT_STAT_FILE;
+}
diff --git a/teensy/lcd.c b/teensy/lcd.c
index cc3f52bad5..5b991aac77 100644
--- a/teensy/lcd.c
+++ b/teensy/lcd.c
@@ -1,5 +1,10 @@
+#include "mpconfig.h"
+#include "nlr.h"
 #include "misc.h"
-#include "../stm/lcd.h"
+#include "qstr.h"
+#include "parse.h"
+#include "obj.h"
+#include "../stmhal/lcd.h"
 
 void lcd_init(void) {
 }
diff --git a/teensy/led.c b/teensy/led.c
index e2a0574168..d825f32198 100644
--- a/teensy/led.c
+++ b/teensy/led.c
@@ -1,53 +1,115 @@
 #include <stdio.h>
 
-#include "misc.h"
-#include "mpconfig.h"
-#include "qstr.h"
-#include "obj.h"
-#include "led.h"
-
 #include "Arduino.h"
 
+#include "mpconfig.h"
+#include HAL_H
+#include "nlr.h"
+#include "misc.h"
+#include "qstr.h"
+#include "obj.h"
+#include "runtime.h"
+#include "led.h"
+#include "pin.h"
+#include "genhdr/pins.h"
+
+
+typedef struct _pyb_led_obj_t {
+    mp_obj_base_t base;
+    machine_uint_t led_id;
+    const pin_obj_t *led_pin;
+} pyb_led_obj_t;
+
+STATIC const pyb_led_obj_t pyb_led_obj[] = {
+    {{&pyb_led_type}, 1, &MICROPY_HW_LED1},
+#if defined(MICROPY_HW_LED2)
+    {{&pyb_led_type}, 2, &MICROPY_HW_LED2},
+#if defined(MICROPY_HW_LED3)
+    {{&pyb_led_type}, 3, &MICROPY_HW_LED3},
+#if defined(MICROPY_HW_LED4)
+    {{&pyb_led_type}, 4, &MICROPY_HW_LED4},
+#endif
+#endif
+#endif
+};
+#define NUM_LEDS ARRAY_SIZE(pyb_led_obj)
+
 void led_init(void) {
+    /* GPIO structure */
+    GPIO_InitTypeDef GPIO_InitStructure;
+
+    /* Configure I/O speed, mode, output type and pull */
+    GPIO_InitStructure.Speed = GPIO_SPEED_LOW;
+    GPIO_InitStructure.Mode = MICROPY_HW_LED_OTYPE;
+    GPIO_InitStructure.Pull = GPIO_NOPULL;
+
+    /* Turn off LEDs and initialize */
+    for (int led = 0; led < NUM_LEDS; led++) {
+        const pin_obj_t *led_pin = pyb_led_obj[led].led_pin;
+        MICROPY_HW_LED_OFF(led_pin);
+        GPIO_InitStructure.Pin = led_pin->pin_mask;
+        HAL_GPIO_Init(led_pin->gpio, &GPIO_InitStructure);
+    }
 }
 
 void led_state(pyb_led_t led, int state) {
-    uint8_t pin;
-
-    if (led == 0) {
-        pin = LED_BUILTIN;
-    } else {
+    if (led < 1 || led > NUM_LEDS) {
         return;
     }
-    digitalWrite(pin, state);
+    const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin;
+    //printf("led_state(%d,%d)\n", led, state);
+    if (state == 0) {
+        // turn LED off
+        MICROPY_HW_LED_OFF(led_pin);
+    } else {
+        // turn LED on
+        MICROPY_HW_LED_ON(led_pin);
+    }
 }
 
 void led_toggle(pyb_led_t led) {
-    uint8_t pin;
-
-    if (led == 0) {
-        pin = LED_BUILTIN;
-    } else {
+    if (led < 1 || led > NUM_LEDS) {
         return;
     }
+    const pin_obj_t *led_pin = pyb_led_obj[led - 1].led_pin;
+    GPIO_TypeDef *gpio = led_pin->gpio;
 
-    digitalWrite(pin, !digitalRead(pin));
+    // We don't know if we're turning the LED on or off, but we don't really
+    // care. Just invert the state.
+    if (gpio->PDOR & led_pin->pin_mask) {
+        // pin is high, make it low
+        gpio->PCOR = led_pin->pin_mask;
+    } else {
+        // pin is low, make it high
+        gpio->PSOR = led_pin->pin_mask;
+    }
 }
 
 /******************************************************************************/
 /* Micro Python bindings                                                      */
 
-typedef struct _pyb_led_obj_t {
-    mp_obj_base_t base;
-    uint led_id;
-} pyb_led_obj_t;
-
 void led_obj_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
     pyb_led_obj_t *self = self_in;
     (void)kind;
     print(env, "<LED %lu>", self->led_id);
 }
 
+STATIC mp_obj_t led_obj_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
+    // check arguments
+    mp_arg_check_num(n_args, n_kw, 1, 1, false);
+
+    // get led number
+    machine_int_t led_id = mp_obj_get_int(args[0]);
+
+    // check led number
+    if (!(1 <= led_id && led_id <= NUM_LEDS)) {
+        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "LED %d does not exist", led_id));
+    }
+
+    // return static led object
+    return (mp_obj_t)&pyb_led_obj[led_id - 1];
+}
+
 mp_obj_t led_obj_on(mp_obj_t self_in) {
     pyb_led_obj_t *self = self_in;
     led_state(self->led_id, 1);
@@ -60,25 +122,28 @@ mp_obj_t led_obj_off(mp_obj_t self_in) {
     return mp_const_none;
 }
 
-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_type_type },
-    .name = MP_QSTR_Led,
-    .print = led_obj_print,
-    .methods = led_methods,
-};
-
-mp_obj_t pyb_Led(mp_obj_t led_id) {
-    pyb_led_obj_t *o = m_new_obj(pyb_led_obj_t);
-    o->base.type = &led_obj_type;
-    o->led_id = mp_obj_get_int(led_id);
-    return o;
+mp_obj_t led_obj_toggle(mp_obj_t self_in) {
+    pyb_led_obj_t *self = self_in;
+    led_toggle(self->led_id);
+    return mp_const_none;
 }
+
+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 MP_DEFINE_CONST_FUN_OBJ_1(led_obj_toggle_obj, led_obj_toggle);
+
+STATIC const mp_map_elem_t led_locals_dict_table[] = {
+    { MP_OBJ_NEW_QSTR(MP_QSTR_on), (mp_obj_t)&led_obj_on_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_off), (mp_obj_t)&led_obj_off_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_toggle), (mp_obj_t)&led_obj_toggle_obj },
+};
+
+STATIC MP_DEFINE_CONST_DICT(led_locals_dict, led_locals_dict_table);
+
+const mp_obj_type_t pyb_led_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_LED,
+    .print = led_obj_print,
+    .make_new = led_obj_make_new,
+    .locals_dict = (mp_obj_t)&led_locals_dict,
+};
diff --git a/teensy/led.h b/teensy/led.h
index c5a4812549..7f4ba18f26 100644
--- a/teensy/led.h
+++ b/teensy/led.h
@@ -1,9 +1,9 @@
 typedef enum {
-    PYB_LED_BUILTIN = 0,
+    PYB_LED_BUILTIN = 1,
 } pyb_led_t;
 
 void led_init(void);
 void led_state(pyb_led_t led, int state);
 void led_toggle(pyb_led_t led);
 
-mp_obj_t pyb_Led(mp_obj_t led_id);
+extern const mp_obj_type_t pyb_led_type;
diff --git a/teensy/lexerfatfs.c b/teensy/lexerfatfs.c
index 95084b6501..84245f8878 100644
--- a/teensy/lexerfatfs.c
+++ b/teensy/lexerfatfs.c
@@ -6,19 +6,10 @@
 #include "qstr.h"
 #include "lexer.h"
 typedef int FIL;
-#include "../stm/lexerfatfs.h"
+#include "../stmhal/lexerfatfs.h"
 
 mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
   printf("import not implemented\n");
   return NULL;
 }
 
-mp_lexer_t *mp_import_open_file(qstr mod_name) {
-    printf("import not implemented\n");
-    return NULL;
-}
-
-mp_import_stat_t mp_import_stat(const char *path) {
-    // TODO implement me!
-    return MP_IMPORT_STAT_NO_EXIST;
-}
diff --git a/teensy/lexermemzip.c b/teensy/lexermemzip.c
index 3e15717acf..f50a3c0808 100644
--- a/teensy/lexermemzip.c
+++ b/teensy/lexermemzip.c
@@ -7,7 +7,7 @@
 #include "lexer.h"
 #include "memzip.h"
 
-mp_lexer_t *mp_lexer_new_from_memzip_file(const char *filename)
+mp_lexer_t *mp_lexer_new_from_file(const char *filename)
 {
     void *data;
     size_t len;
diff --git a/teensy/main.c b/teensy/main.c
index eb153c245d..c22382675d 100644
--- a/teensy/main.c
+++ b/teensy/main.c
@@ -3,30 +3,33 @@
 #include <string.h>
 #include <stdlib.h>
 
-#include "nlr.h"
 #include "misc.h"
 #include "mpconfig.h"
 #include "qstr.h"
+#include "nlr.h"
 #include "lexer.h"
 #include "lexermemzip.h"
 #include "parse.h"
 #include "obj.h"
-#include "compile.h"
-#include "runtime0.h"
 #include "runtime.h"
-#include "repl.h"
-#include "servo.h"
-#include "usb.h"
 #include "gc.h"
-#include "led.h"
-#include "build/py/py-version.h"
+#include "gccollect.h"
+#include "pyexec.h"
+#include "pybstdio.h"
+#include "readline.h"
 
 #include "Arduino.h"
+#include HAL_H
+
+#include "servo.h"
+#include "usb.h"
+#include "led.h"
+
+//#include "pin.h"
+
 
 extern uint32_t _heap_start;
 
-bool do_file(const char *filename);
-
 void flash_error(int n) {
     for (int i = 0; i < n; i++) {
         led_state(PYB_LED_BUILTIN, 1);
@@ -36,25 +39,36 @@ void flash_error(int n) {
     }
 }
 
-static const char *help_text =
-"Welcome to Micro Python!\n\n"
-"This is a *very* early version of Micro Python and has minimal functionality.\n\n"
-"Specific commands for the board:\n"
-"    pyb.info()             -- print some general information\n"
-"    pyb.gc()               -- run the garbage collector\n"
-"    pyb.delay(<n>)         -- wait for n milliseconds\n"
-"    pyb.Led(<n>)           -- create Led object for LED n (n=0)\n"
-"                              Led methods: on(), off()\n"
-"    pyb.gpio(<pin>)        -- read gpio pin\n"
-"    pyb.gpio(<pin>, <val>) -- set gpio pin\n"
-#if 0
-"    pyb.Servo(<n>) -- create Servo object for servo n (n=1,2,3,4)\n"
-"                      Servo methods: angle(<x>)\n"
-"    pyb.switch()   -- return True/False if switch pressed or not\n"
-"    pyb.accel()    -- get accelerometer values\n"
-"    pyb.rand()     -- get a 16-bit random number\n"
-#endif
-;
+void __fatal_error(const char *msg) {
+    for (volatile uint delay = 0; delay < 10000000; delay++) {
+    }
+    led_state(1, 1);
+    led_state(2, 1);
+    led_state(3, 1);
+    led_state(4, 1);
+    stdout_tx_strn("\nFATAL ERROR:\n", 14);
+    stdout_tx_strn(msg, strlen(msg));
+    for (uint i = 0;;) {
+        led_toggle(((i++) & 3) + 1);
+        for (volatile uint delay = 0; delay < 10000000; delay++) {
+        }
+        if (i >= 16) {
+            // to conserve power
+            __WFI();
+        }
+    }
+}
+
+void nlr_jump_fail(void *val) {
+    printf("FATAL: uncaught exception %p\n", val);
+    __fatal_error("");
+}
+
+void __assert_func(const char *file, int line, const char *func, const char *expr) {
+
+    printf("Assertion failed: %s, file %s, line %d\n", expr, file, line);
+    __fatal_error("");
+}
 
 mp_obj_t pyb_analog_read(mp_obj_t pin_obj) {
     uint pin = mp_obj_get_int(pin_obj);
@@ -82,12 +96,7 @@ mp_obj_t pyb_analog_write_frequency(mp_obj_t pin_obj, mp_obj_t freq_obj) {
     return mp_const_none;
 }
 
-// get some help about available functions
-static mp_obj_t pyb_help(void) {
-    printf("%s", help_text);
-    return mp_const_none;
-}
-
+#if 0
 // get lots of info about the board
 static mp_obj_t pyb_info(void) {
     // get and print unique id; 96 bits
@@ -101,12 +110,6 @@ static mp_obj_t pyb_info(void) {
 
     // to print info about memory
     {
-        extern void *_sdata;
-        extern void *_edata;
-        extern void *_sbss;
-        extern void *_ebss;
-        extern void *_estack;
-        extern void *_etext;
         printf("_sdata=%p\n", &_sdata);
         printf("_edata=%p\n", &_edata);
         printf("_sbss=%p\n", &_sbss);
@@ -121,9 +124,9 @@ static mp_obj_t pyb_info(void) {
         gc_info_t info;
         gc_info(&info);
         printf("GC:\n");
-        printf("  %lu total\n", info.total);
-        printf("  %lu used %lu free\n", info.used, info.free);
-        printf("  1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block);
+        printf("  %u total\n", info.total);
+        printf("  %u used %u free\n", info.used, info.free);
+        printf("  1=%u 2=%u m=%u\n", info.num_1block, info.num_2block, info.max_block);
     }
 
 #if 0
@@ -139,33 +142,16 @@ static mp_obj_t pyb_info(void) {
     return mp_const_none;
 }
 
+#endif
+
 #define RAM_START (0x1FFF8000) // fixed for chip
 #define HEAP_END  (0x20006000) // tunable
 #define RAM_END   (0x20008000) // fixed for chip
 
+#if 0
+
 void gc_helper_get_regs_and_clean_stack(machine_uint_t *regs, machine_uint_t heap_end);
 
-void gc_collect(void) {
-    uint32_t start = micros();
-    gc_collect_start();
-    gc_collect_root((void**)RAM_START, (((uint32_t)&_heap_start) - RAM_START) / 4);
-    machine_uint_t regs[10];
-    gc_helper_get_regs_and_clean_stack(regs, HEAP_END);
-    gc_collect_root((void**)HEAP_END, (RAM_END - HEAP_END) / 4); // will trace regs since they now live in this function on the stack
-    gc_collect_end();
-    uint32_t ticks = micros() - start; // TODO implement a function that does this properly
-
-    if (0) {
-        // print GC info
-        gc_info_t info;
-        gc_info(&info);
-        printf("GC@%lu %luus\n", start, ticks);
-        printf(" %lu total\n", info.total);
-        printf(" %lu used %lu free\n", info.used, info.free);
-        printf(" 1=%lu 2=%lu m=%lu\n", info.num_1block, info.num_2block, info.max_block);
-    }
-}
-
 mp_obj_t pyb_gc(void) {
     gc_collect();
     return mp_const_none;
@@ -191,7 +177,7 @@ mp_obj_t pyb_gpio(int n_args, mp_obj_t *args) {
     return mp_const_none;
 
 pin_error:
-    nlr_raise(mp_obj_new_exception_msg_varg(MP_QSTR_ValueError, "pin %d does not exist", pin));
+    nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "pin %d does not exist", pin));
 }
 
 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_gpio_obj, 1, 2, pyb_gpio);
@@ -209,29 +195,41 @@ mp_obj_t pyb_hid_send_report(mp_obj_t arg) {
 }
 #endif
 
-static mp_obj_t pyb_config_source_dir = MP_OBJ_NULL;
-static mp_obj_t pyb_config_main = MP_OBJ_NULL;
+#endif // 0
+
+STATIC mp_obj_t pyb_config_source_dir = MP_OBJ_NULL;
+STATIC mp_obj_t pyb_config_main = MP_OBJ_NULL;
+STATIC mp_obj_t pyb_config_usb_mode = MP_OBJ_NULL;
 
 mp_obj_t pyb_source_dir(mp_obj_t source_dir) {
     if (MP_OBJ_IS_STR(source_dir)) {
         pyb_config_source_dir = source_dir;
-        printf("source_dir = '");
-        mp_obj_print(source_dir, PRINT_STR);
-        printf("'\n");
     }
     return mp_const_none;
 }
 
+MP_DEFINE_CONST_FUN_OBJ_1(pyb_source_dir_obj, pyb_source_dir);
+
 mp_obj_t pyb_main(mp_obj_t main) {
     if (MP_OBJ_IS_STR(main)) {
         pyb_config_main = main;
-        printf("main = '");
-        mp_obj_print(main, PRINT_STR);
-        printf("'\n");
     }
     return mp_const_none;
 }
 
+MP_DEFINE_CONST_FUN_OBJ_1(pyb_main_obj, pyb_main);
+
+STATIC mp_obj_t pyb_usb_mode(mp_obj_t usb_mode) {
+    if (MP_OBJ_IS_STR(usb_mode)) {
+        pyb_config_usb_mode = usb_mode;
+    }
+    return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(pyb_usb_mode_obj, pyb_usb_mode);
+
+#if 0
+
 mp_obj_t pyb_delay(mp_obj_t count) {
     delay(mp_obj_get_int(count));
     return mp_const_none;
@@ -242,12 +240,9 @@ mp_obj_t pyb_led(mp_obj_t state) {
     return state;
 }
 
-mp_obj_t pyb_run(mp_obj_t filename_obj) {
-    const char *filename = qstr_str(mp_obj_str_get_qstr(filename_obj));
-    do_file(filename);
-    return mp_const_none;
-}
+#endif  // 0
 
+#if 0
 char *strdup(const char *str) {
     uint32_t len = strlen(str);
     char *s2 = m_new(char, len + 1);
@@ -255,219 +250,31 @@ char *strdup(const char *str) {
     s2[len] = 0;
     return s2;
 }
-
-#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);
-}
-
-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;
-        for (;;) {
-            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);
-    }
-}
-
-bool do_file(const char *filename) {
-    mp_lexer_t *lex = mp_lexer_new_from_memzip_file(filename);
-
-    if (lex == NULL) {
-        printf("could not open file '%s' for reading\n", filename);
-        return false;
-    }
-
-    mp_parse_error_kind_t parse_error_kind;
-    mp_parse_node_t pn = mp_parse(lex, MP_PARSE_FILE_INPUT, &parse_error_kind);
-    qstr source_name = mp_lexer_source_name(lex);
-
-    if (pn == MP_PARSE_NODE_NULL) {
-        // parse error
-        mp_parse_show_exception(lex, parse_error_kind);
-        mp_lexer_free(lex);
-        return false;
-    }
-
-    mp_lexer_free(lex);
-
-    mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, false);
-    mp_parse_node_free(pn);
-
-    if (module_fun == mp_const_none) {
-        return false;
-    }
-
-    nlr_buf_t nlr;
-    if (nlr_push(&nlr) == 0) {
-        mp_call_function_0(module_fun);
-        nlr_pop();
-        return true;
-    } else {
-        // uncaught exception
-        mp_obj_print((mp_obj_t)nlr.ret_val, PRINT_REPR);
-        printf("\n");
-        return false;
-    }
-}
-
-void do_repl(void) {
-    stdout_tx_str("Micro Python build " MICROPY_GIT_HASH " on " MICROPY_BUILD_DATE "; Teensy 3.1 version\n");
-    stdout_tx_str("Type \"help()\" for more information.\r\n");
-
-    vstr_t line;
-    vstr_init(&line, 32);
-
-    for (;;) {
-        vstr_reset(&line);
-        int ret = readline(&line, ">>> ");
-        if (ret == 0) {
-            // EOF
-            break;
-        }
-
-        if (vstr_len(&line) == 0) {
-            continue;
-        }
-
-        while (mp_repl_continue_with_input(vstr_str(&line))) {
-            vstr_add_char(&line, '\n');
-            int ret = readline(&line, "... ");
-            if (ret == 0) {
-                // stop entering compound statement
-                break;
-            }
-        }
-
-        mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr_str(&line), vstr_len(&line), 0);
-        mp_parse_error_kind_t parse_error_kind;
-        mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT, &parse_error_kind);
-        qstr source_name = mp_lexer_source_name(lex);
-
-        if (pn == MP_PARSE_NODE_NULL) {
-            // parse error
-            mp_parse_show_exception(lex, parse_error_kind);
-            mp_lexer_free(lex);
-        } else {
-            // parse okay
-            mp_lexer_free(lex);
-            mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, true);
-            if (module_fun != mp_const_none) {
-                nlr_buf_t nlr;
-                uint32_t start = micros();
-                if (nlr_push(&nlr) == 0) {
-                    mp_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 ms)\n", ticks);
-                    }
-                } else {
-                    // uncaught exception
-                    mp_obj_print((mp_obj_t)nlr.ret_val, PRINT_REPR);
-                    printf("\n");
-                }
-            }
-        }
-    }
-
-    stdout_tx_str("\r\n");
-}
 
 int main(void) {
     pinMode(LED_BUILTIN, OUTPUT);
-#if 0
-    // Wait for host side to get connected
-    while (!usb_vcp_is_connected()) {
-        ;
-    }
-#else
     delay(1000);
-#endif
 
     led_init();
-    led_state(PYB_LED_BUILTIN, 1);
 
 //    int first_soft_reset = true;
 
 soft_reset:
 
+    led_state(PYB_LED_BUILTIN, 1);
+
     // GC init
     gc_init(&_heap_start, (void*)HEAP_END);
 
     qstr_init();
     mp_init();
 
+    readline_init();
+
+    //pin_init();
+
+#if 0
     // add some functions to the python namespace
     {
         mp_store_name(MP_QSTR_help, mp_make_function_n(0, pyb_help));
@@ -478,7 +285,7 @@ soft_reset:
         mp_store_attr(m, MP_QSTR_gc, mp_make_function_n(0, pyb_gc));
         mp_store_attr(m, MP_QSTR_delay, mp_make_function_n(1, pyb_delay));
         mp_store_attr(m, MP_QSTR_led, mp_make_function_n(1, pyb_led));
-        mp_store_attr(m, MP_QSTR_Led, mp_make_function_n(1, pyb_Led));
+        mp_store_attr(m, MP_QSTR_LED, (mp_obj_t)&pyb_led_type);
         mp_store_attr(m, MP_QSTR_analogRead, mp_make_function_n(1, pyb_analog_read));
         mp_store_attr(m, MP_QSTR_analogWrite, mp_make_function_n(2, pyb_analog_write));
         mp_store_attr(m, MP_QSTR_analogWriteResolution, mp_make_function_n(1, pyb_analog_write_resolution));
@@ -487,15 +294,12 @@ soft_reset:
         mp_store_attr(m, MP_QSTR_gpio, (mp_obj_t)&pyb_gpio_obj);
         mp_store_attr(m, MP_QSTR_Servo, mp_make_function_n(0, pyb_Servo));
         mp_store_name(MP_QSTR_pyb, m);
-        mp_store_name(MP_QSTR_run, mp_make_function_n(1, pyb_run));
     }
+#endif
 
-    printf("About execute /boot.py\n");
-    if (!do_file("/boot.py")) {
-        printf("Unable to open '/boot.py'\n");
+    if (!pyexec_file("/boot.py")) {
         flash_error(4);
     }
-    printf("Done executing /boot.py\n");
 
     // Turn bootup LED off
     led_state(PYB_LED_BUILTIN, 0);
@@ -504,27 +308,30 @@ soft_reset:
     {
         vstr_t *vstr = vstr_new();
         vstr_add_str(vstr, "/");
-        if (pyb_config_source_dir == MP_OBJ_NULL) {
-            vstr_add_str(vstr, "src");
-        } else {
-            vstr_add_str(vstr, mp_obj_str_get_str(pyb_config_source_dir));
-        }
-        vstr_add_char(vstr, '/');
         if (pyb_config_main == MP_OBJ_NULL) {
             vstr_add_str(vstr, "main.py");
         } else {
             vstr_add_str(vstr, mp_obj_str_get_str(pyb_config_main));
         }
-        printf("About execute '%s'\n", vstr_str(vstr));
-        if (!do_file(vstr_str(vstr))) {
-            printf("Unable to open '%s'\n", vstr_str(vstr));
+        if (!pyexec_file(vstr_str(vstr))) {
             flash_error(3);
         }
-        printf("Done executing '%s'\n", vstr_str(vstr));
         vstr_free(vstr);
     }
 
-    do_repl();
+    // enter REPL
+    // REPL mode can change, or it can request a soft reset
+    for (;;) {
+        if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
+            if (pyexec_raw_repl() != 0) {
+                break;
+            }
+        } else {
+            if (pyexec_friendly_repl() != 0) {
+                break;
+            }
+        }
+    }
 
     printf("PYB: soft reboot\n");
 
@@ -532,22 +339,15 @@ soft_reset:
     goto soft_reset;
 }
 
-double sqrt(double x) {
-    // TODO
-    return 0.0;
-}
-
-machine_float_t machine_sqrt(machine_float_t x) {
-    // TODO
-    return x;
-}
-
 // stub out __libc_init_array. It's called by mk20dx128.c and is used to call
 // global C++ constructors. Since this is a C-only projects, we don't need to
 // call constructors.
 void __libc_init_array(void) {
 }
 
+// ultoa is used by usb_init_serialnumber. Normally ultoa would be provided
+// by nonstd.c from the teensy core, but it conflicts with some of the
+// MicroPython functions in string0.c, so we provide ultoa here.
 char * ultoa(unsigned long val, char *buf, int radix) 	
 {
 	unsigned digit;
@@ -569,3 +369,12 @@ char * ultoa(unsigned long val, char *buf, int radix)
 	}
 	return buf;
 }
+
+STATIC NORETURN mp_obj_t mp_sys_exit(uint n_args, const mp_obj_t *args) {
+    int rc = 0;
+    if (n_args > 0) {
+        rc = mp_obj_get_int(args[0]);
+    }
+    nlr_raise(mp_obj_new_exception_arg1(&mp_type_SystemExit, mp_obj_new_int(rc)));
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_exit_obj, 0, 1, mp_sys_exit);
diff --git a/teensy/make-pins.py b/teensy/make-pins.py
new file mode 100755
index 0000000000..1fd05ec207
--- /dev/null
+++ b/teensy/make-pins.py
@@ -0,0 +1,326 @@
+#!/usr/bin/env python
+"""Creates the pin file for the Teensy."""
+
+from __future__ import print_function
+
+import argparse
+import sys
+import csv
+
+SUPPORTED_FN = {
+    'FTM'   : ['CH0',  'CH1',  'CH2',  'CH3',
+               'QD_PHA', 'QD_PHB'],
+    'I2C'   : ['SDA', 'SCL'],
+    'UART'  : ['RX', 'TX', 'CTS', 'RTS'],
+    'SPI'   : ['NSS', 'SCK', 'MISO', 'MOSI']
+}
+
+def parse_port_pin(name_str):
+    """Parses a string and returns a (port-num, pin-num) tuple."""
+    if len(name_str) < 4:
+        raise ValueError("Expecting pin name to be at least 4 charcters.")
+    if name_str[0:2] != 'PT':
+        raise ValueError("Expecting pin name to start with PT")
+    if name_str[2] not in ('A', 'B', 'C', 'D', 'E', 'Z'):
+        raise ValueError("Expecting pin port to be between A and E or Z")
+    port = ord(name_str[2]) - ord('A')
+    pin_str = name_str[3:].split('/')[0]
+    if not pin_str.isdigit():
+        raise ValueError("Expecting numeric pin number.")
+    return (port, int(pin_str))
+
+def split_name_num(name_num):
+    num = None
+    for num_idx in range(len(name_num) - 1, -1, -1):
+        if not name_num[num_idx].isdigit():
+            name = name_num[0:num_idx + 1]
+            num_str = name_num[num_idx + 1:]
+            if len(num_str) > 0:
+                num = int(num_str)
+            break
+    return name, num
+
+
+class AlternateFunction(object):
+    """Holds the information associated with a pins alternate function."""
+
+    def __init__(self, idx, af_str):
+        self.idx = idx
+        self.af_str = af_str
+
+        self.func = ''
+        self.fn_num = None
+        self.pin_type = ''
+        self.supported = False
+
+        af_words = af_str.split('_', 1)
+        self.func, self.fn_num = split_name_num(af_words[0])
+        if len(af_words) > 1:
+            self.pin_type = af_words[1]
+        if self.func in SUPPORTED_FN:
+            pin_types = SUPPORTED_FN[self.func]
+            if self.pin_type in pin_types:
+                self.supported = True
+
+    def is_supported(self):
+        return self.supported
+
+    def ptr(self):
+        """Returns the numbered function (i.e. USART6) for this AF."""
+        if self.fn_num is None:
+            return self.func
+        return '{:s}{:d}'.format(self.func, self.fn_num)
+
+    def print(self):
+        """Prints the C representation of this AF."""
+        if self.supported:
+            print('  AF',  end='')
+        else:
+            print('  //', end='')
+        fn_num = self.fn_num
+        if fn_num is None:
+            fn_num = 0
+        print('({:2d}, {:8s}, {:2d}, {:10s}, {:8s}), // {:s}'.format(self.idx,
+              self.func, fn_num, self.pin_type, self.ptr(), self.af_str))
+
+
+class Pin(object):
+    """Holds the information associated with a pin."""
+
+    def __init__(self, port, pin):
+        self.port = port
+        self.pin = pin
+        self.alt_fn = []
+        self.alt_fn_count = 0
+        self.adc_num = 0
+        self.adc_channel = 0
+        self.board_pin = False
+
+    def port_letter(self):
+        return chr(self.port + ord('A'))
+
+    def cpu_pin_name(self):
+        return '{:s}{:d}'.format(self.port_letter(), self.pin)
+
+    def is_board_pin(self):
+        return self.board_pin
+
+    def set_is_board_pin(self):
+        self.board_pin = True
+
+    def parse_adc(self, adc_str):
+        if (adc_str[:3] != 'ADC'):
+            return
+        (adc,channel) = adc_str.split('_')
+        for idx in range(3, len(adc)):
+            adc_num = int(adc[idx]) # 1, 2, or 3
+            self.adc_num |= (1 << (adc_num - 1))
+        self.adc_channel = int(channel[2:])
+
+    def parse_af(self, af_idx, af_strs_in):
+        if len(af_strs_in) == 0:
+            return
+        # If there is a slash, then the slash separates 2 aliases for the
+        # same alternate function.
+        af_strs = af_strs_in.split('/')
+        for af_str in af_strs:
+            alt_fn = AlternateFunction(af_idx, af_str)
+            self.alt_fn.append(alt_fn)
+            if alt_fn.is_supported():
+                self.alt_fn_count += 1
+
+    def alt_fn_name(self, null_if_0=False):
+        if null_if_0 and self.alt_fn_count == 0:
+            return 'NULL'
+        return 'pin_{:s}_af'.format(self.cpu_pin_name())
+
+    def adc_num_str(self):
+        str = ''
+        for adc_num in range(1,4):
+            if self.adc_num & (1 << (adc_num - 1)):
+                if len(str) > 0:
+                    str += ' | '
+                str += 'PIN_ADC'
+                str += chr(ord('0') + adc_num)
+        if len(str) == 0:
+            str = '0'
+        return str
+
+    def print(self):
+        if self.alt_fn_count == 0:
+            print("// ",  end='')
+        print('const pin_af_obj_t {:s}[] = {{'.format(self.alt_fn_name()))
+        for alt_fn in self.alt_fn:
+            alt_fn.print()
+        if self.alt_fn_count == 0:
+            print("// ",  end='')
+        print('};')
+        print('')
+        print('const pin_obj_t pin_{:s} = PIN({:s}, {:d}, {:d}, {:s}, {:s}, {:d});'.format(
+            self.cpu_pin_name(), self.port_letter(), self.pin,
+            self.alt_fn_count, self.alt_fn_name(null_if_0=True),
+            self.adc_num_str(), self.adc_channel))
+        print('')
+
+    def print_header(self, hdr_file):
+        hdr_file.write('extern const pin_obj_t pin_{:s};\n'.
+                       format(self.cpu_pin_name()))
+        if self.alt_fn_count > 0:
+            hdr_file.write('extern const pin_af_obj_t pin_{:s}_af[];\n'.
+                           format(self.cpu_pin_name()))
+
+class NamedPin(object):
+
+    def __init__(self, name, pin):
+        self._name = name
+        self._pin = pin
+
+    def pin(self):
+        return self._pin
+
+    def name(self):
+        return self._name
+
+
+class Pins(object):
+
+    def __init__(self):
+        self.cpu_pins = []   # list of NamedPin objects
+        self.board_pins = [] # list of NamedPin objects
+
+    def find_pin(self, port_num, pin_num):
+        for named_pin in self.cpu_pins:
+            pin = named_pin.pin()
+            if pin.port == port_num and pin.pin == pin_num:
+                return pin
+
+    def parse_af_file(self, filename, pinname_col, af_col):
+        with open(filename, 'r') as csvfile:
+            rows = csv.reader(csvfile)
+            for row in rows:
+                try:
+                    (port_num, pin_num) = parse_port_pin(row[pinname_col])
+                except:
+                    continue
+                pin = Pin(port_num, pin_num)
+                for af_idx in range(af_col, len(row)):
+                    if af_idx >= af_col:
+                        pin.parse_af(af_idx - af_col, row[af_idx])
+                self.cpu_pins.append(NamedPin(pin.cpu_pin_name(), pin))
+
+    def parse_board_file(self, filename):
+        with open(filename, 'r') as csvfile:
+            rows = csv.reader(csvfile)
+            for row in rows:
+                try:
+                    (port_num, pin_num) = parse_port_pin(row[1])
+                except:
+                    continue
+                pin = self.find_pin(port_num, pin_num)
+                if pin:
+                    pin.set_is_board_pin()
+                    self.board_pins.append(NamedPin(row[0], pin))
+
+    def print_named(self, label, named_pins):
+        print('const pin_named_pin_t pin_{:s}_pins[] = {{'.format(label))
+        for named_pin in named_pins:
+            pin = named_pin.pin()
+            if pin.is_board_pin():
+                print('  {{ "{:s}", &pin_{:s} }},'.format(named_pin.name(),  pin.cpu_pin_name()))
+        print('  { NULL, NULL }')
+        print('};')
+
+    def print(self):
+        for named_pin in self.cpu_pins:
+            pin = named_pin.pin()
+            if pin.is_board_pin():
+                pin.print()
+        self.print_named('cpu', self.cpu_pins)
+        print('')
+        self.print_named('board', self.board_pins)
+
+    def print_adc(self, adc_num):
+        print('');
+        print('const pin_obj_t * const pin_adc{:d}[] = {{'.format(adc_num))
+        for channel in range(16):
+            adc_found = False
+            for named_pin in self.cpu_pins:
+                pin = named_pin.pin()
+                if (pin.is_board_pin() and
+                    (pin.adc_num & (1 << (adc_num - 1))) and (pin.adc_channel == channel)):
+                    print('  &pin_{:s}, // {:d}'.format(pin.cpu_pin_name(), channel))
+                    adc_found = True
+                    break
+            if not adc_found:
+                print('  NULL,    // {:d}'.format(channel))
+        print('};')
+
+
+    def print_header(self, hdr_filename):
+        with open(hdr_filename, 'wt') as hdr_file:
+            for named_pin in self.cpu_pins:
+                pin = named_pin.pin()
+                if pin.is_board_pin():
+                    pin.print_header(hdr_file)
+            hdr_file.write('extern const pin_obj_t * const pin_adc1[];\n')
+            hdr_file.write('extern const pin_obj_t * const pin_adc2[];\n')
+            hdr_file.write('extern const pin_obj_t * const pin_adc3[];\n')
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        prog="make-pins.py",
+        usage="%(prog)s [options] [command]",
+        description="Generate board specific pin file"
+    )
+    parser.add_argument(
+        "-a", "--af",
+        dest="af_filename",
+        help="Specifies the alternate function file for the chip",
+        default="stm32f4xx-af.csv"
+    )
+    parser.add_argument(
+        "-b", "--board",
+        dest="board_filename",
+        help="Specifies the board file",
+    )
+    parser.add_argument(
+        "-p", "--prefix",
+        dest="prefix_filename",
+        help="Specifies beginning portion of generated pins file",
+        default="stm32f4xx-prefix.c"
+    )
+    parser.add_argument(
+        "-r", "--hdr",
+        dest="hdr_filename",
+        help="Specifies name of generated pin header file",
+        default="build/pins.h"
+    )
+    args = parser.parse_args(sys.argv[1:])
+
+    pins = Pins()
+
+    print('// This file was automatically generated by make-pins.py')
+    print('//')
+    if args.af_filename:
+        print('// --af {:s}'.format(args.af_filename))
+        pins.parse_af_file(args.af_filename, 4, 3)
+
+    if args.board_filename:
+        print('// --board {:s}'.format(args.board_filename))
+        pins.parse_board_file(args.board_filename)
+
+    if args.prefix_filename:
+        print('// --prefix {:s}'.format(args.prefix_filename))
+        print('')
+        with open(args.prefix_filename, 'r') as prefix_file:
+            print(prefix_file.read())
+    pins.print()
+    pins.print_adc(1)
+    pins.print_adc(2)
+    pins.print_adc(3)
+    pins.print_header(args.hdr_filename)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/teensy/memzip.c b/teensy/memzip.c
index ec6c26980c..b269c472a1 100644
--- a/teensy/memzip.c
+++ b/teensy/memzip.c
@@ -1,12 +1,13 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include "misc.h"
 #include "memzip.h"
 
 extern uint8_t _staticfs[];
 
-MEMZIP_RESULT memzip_locate(const char *filename, void **data, size_t *len)
-{
+const MEMZIP_FILE_HDR *memzip_find_file_header(const char *filename) {
+
     const MEMZIP_FILE_HDR *file_hdr = (const MEMZIP_FILE_HDR *)_staticfs;
     uint8_t *mem_data;
 
@@ -22,16 +23,83 @@ MEMZIP_RESULT memzip_locate(const char *filename, void **data, size_t *len)
         mem_data += file_hdr->extra_len;
         if (!strncmp(file_hdr_filename, filename, file_hdr->filename_len)) {
             /* We found a match */
-            if (file_hdr->compression_method != 0) {
-                return MZ_FILE_COMPRESSED;
-            }
-
-            *data = mem_data;
-            *len = file_hdr->uncompressed_size;
-            return MZ_OK;
+            return file_hdr;
         }
         mem_data += file_hdr->uncompressed_size;
         file_hdr = (const MEMZIP_FILE_HDR *)mem_data;
     }
-    return MZ_NO_FILE;
+    return NULL;
+}
+
+bool memzip_is_dir(const char *filename) {
+    const MEMZIP_FILE_HDR *file_hdr = (const MEMZIP_FILE_HDR *)_staticfs;
+    uint8_t *mem_data;
+
+    if (strcmp(filename, "/") == 0) {
+        // The root directory is a directory.
+        return true;
+    }
+
+    // Zip filenames don't have a leading /, so we strip it off
+    if (*filename == '/') {
+        filename++;
+    }
+    size_t filename_len = strlen(filename);
+
+    while (file_hdr->signature == MEMZIP_FILE_HEADER_SIGNATURE) {
+        const char *file_hdr_filename = (const char *)&file_hdr[1];
+        if (filename_len < file_hdr->filename_len &&
+            strncmp(file_hdr_filename, filename, filename_len) == 0 &&
+            file_hdr_filename[filename_len] == '/') {
+            return true;
+        }
+
+        mem_data = (uint8_t *)file_hdr_filename;
+        mem_data += file_hdr->filename_len;
+        mem_data += file_hdr->extra_len;
+        mem_data += file_hdr->uncompressed_size;
+        file_hdr = (const MEMZIP_FILE_HDR *)mem_data;
+    }
+    return NULL;
+
+}
+
+MEMZIP_RESULT memzip_locate(const char *filename, void **data, size_t *len)
+{
+    const MEMZIP_FILE_HDR *file_hdr = memzip_find_file_header(filename);
+    if (file_hdr == NULL) {
+        return MZ_NO_FILE;
+    }
+    if (file_hdr->compression_method != 0) {
+        return MZ_FILE_COMPRESSED;
+    }
+
+    uint8_t *mem_data;
+    mem_data = (uint8_t *)&file_hdr[1];
+    mem_data += file_hdr->filename_len;
+    mem_data += file_hdr->extra_len;
+
+    *data = mem_data;
+    *len = file_hdr->uncompressed_size;
+    return MZ_OK;
+}
+
+MEMZIP_RESULT memzip_stat(const char *path, MEMZIP_FILE_INFO *info) {
+    const MEMZIP_FILE_HDR *file_hdr = memzip_find_file_header(path);
+    if (file_hdr == NULL) {
+        if (memzip_is_dir(path)) {
+            info->file_size = 0;
+            info->last_mod_date = 0;
+            info->last_mod_time = 0;
+            info->is_dir = 1;
+            return MZ_OK;
+        }
+        return MZ_NO_FILE;
+    }
+    info->file_size = file_hdr->uncompressed_size;
+    info->last_mod_date = file_hdr->last_mod_date;
+    info->last_mod_time = file_hdr->last_mod_time;
+    info->is_dir = 0;
+
+    return MZ_OK;
 }
diff --git a/teensy/memzip.h b/teensy/memzip.h
index ecbd98763c..667e2df7e1 100644
--- a/teensy/memzip.h
+++ b/teensy/memzip.h
@@ -70,4 +70,14 @@ typedef enum {
 
 } MEMZIP_RESULT;
 
+typedef struct {
+    uint32_t    file_size;
+    uint16_t    last_mod_date;
+    uint16_t    last_mod_time;
+    uint8_t     is_dir;
+
+} MEMZIP_FILE_INFO;
+
 MEMZIP_RESULT memzip_locate(const char *filename, void **data, size_t *len);
+
+MEMZIP_RESULT memzip_stat(const char *path, MEMZIP_FILE_INFO *info);
diff --git a/teensy/memzip_files/boot.py b/teensy/memzip_files/boot.py
index 3fe9f05e53..4702d7b5d3 100644
--- a/teensy/memzip_files/boot.py
+++ b/teensy/memzip_files/boot.py
@@ -1 +1,2 @@
+import pyb
 print("Executing boot.py")
diff --git a/teensy/memzip_files/src/main.py b/teensy/memzip_files/main.py
similarity index 57%
rename from teensy/memzip_files/src/main.py
rename to teensy/memzip_files/main.py
index 8fc08db15e..4f30f2fc5f 100644
--- a/teensy/memzip_files/src/main.py
+++ b/teensy/memzip_files/main.py
@@ -1,11 +1,13 @@
 print("Executing main.py")
 
-x=pyb.led(1)
+led = pyb.LED(1)
+
+led.on()
 pyb.delay(100)
-x=pyb.led(0)
+led.off()
 pyb.delay(100)
-x=pyb.led(1)
+led.on()
 pyb.delay(100)
-x=pyb.led(0)
+led.off()
 
 
diff --git a/teensy/memzip_files/src/test.py b/teensy/memzip_files/src/test.py
deleted file mode 100644
index 7957b27caf..0000000000
--- a/teensy/memzip_files/src/test.py
+++ /dev/null
@@ -1 +0,0 @@
-print("Executing /src/test.py")
diff --git a/teensy/memzip_files/test.py b/teensy/memzip_files/test.py
deleted file mode 100644
index 088cb4383a..0000000000
--- a/teensy/memzip_files/test.py
+++ /dev/null
@@ -1 +0,0 @@
-print("Executing /test.py")
diff --git a/teensy/mk20dx256-af.csv b/teensy/mk20dx256-af.csv
new file mode 100644
index 0000000000..3015c6c7a1
--- /dev/null
+++ b/teensy/mk20dx256-af.csv
@@ -0,0 +1,65 @@
+Pin,Name,Default,ALT0,ALT1,ALT2,ALT3,ALT4,ALT5,ALT6,ALT7,EzPort
+1,PTE0,ADC1_SE4a,ADC1_SE4a,PTE0,SPI1_PCS1,UART1_TX,,,I2C1_SDA,RTC_CLKOUT,
+2,PTE1/LLWU_P0,ADC1_SE5a,ADC1_SE5a,PTE1/LLWU_P0,SPI1_SOUT,UART1_RX,,,I2C1_SCL,SPI1_SIN,
+3,VDD,VDD,VDD,,,,,,,,
+4,VSS,VSS,VSS,,,,,,,,
+5,USB0_DP,USB0_DP,USB0_DP,,,,,,,,
+6,USB0_DM,USB0_DM,USB0_DM,,,,,,,,
+7,VOUT33,VOUT33,VOUT33,,,,,,,,
+8,VREGIN,VREGIN,VREGIN,,,,,,,,
+9,PGA0_DP/ADC0_DP0/ADC1_DP3,PGA0_DP/ADC0_DP0/ADC1_DP3,PGA0_DP/ADC0_DP0/ADC1_DP3,PTZ0,,,,,,,
+10,PGA0_DM/ADC0_DM0/ADC1_DM3,PGA0_DM/ADC0_DM0/ADC1_DM3,PGA0_DM/ADC0_DM0/ADC1_DM3,PTZ1,,,,,,,
+11,PGA1_DP/ADC1_DP0/ADC0_DP3,PGA1_DP/ADC1_DP0/ADC0_DP3,PGA1_DP/ADC1_DP0/ADC0_DP3,PTZ2,,,,,,,
+12,PGA1_DM/ADC1_DM0/ADC0_DM3,PGA1_DM/ADC1_DM0/ADC0_DM3,PGA1_DM/ADC1_DM0/ADC0_DM3,PTZ3,,,,,,,
+13,VDDA,VDDA,VDDA,,,,,,,,
+14,VREFH,VREFH,VREFH,,,,,,,,
+15,VREFL,VREFL,VREFL,,,,,,,,
+16,VSSA,VSSA,VSSA,,,,,,,,
+17,VREF_OUT/CMP1_IN5/CMP0_IN5/ADC1_SE18,VREF_OUT/CMP1_IN5/CMP0_IN5/ADC1_SE18,VREF_OUT/CMP1_IN5/CMP0_IN5/ADC1_SE18,PTZ4,,,,,,,
+18,DAC0_OUT/CMP1_IN3/ADC0_SE23,DAC0_OUT/CMP1_IN3/ADC0_SE23,DAC0_OUT/CMP1_IN3/ADC0_SE23,PTZ5,,,,,,,
+19,XTAL32,XTAL32,XTAL32,,,,,,,,
+20,EXTAL32,EXTAL32,EXTAL32,,,,,,,,
+21,VBAT,VBAT,VBAT,,,,,,,,
+22,PTA0,JTAG_TCLK/SWD_CLK/EZP_CLK,TSI0_CH1,PTA0,UART0_CTS_b/UART0_COL_b,FTM0_CH5,,,,JTAG_TCLK/SWD_CLK,EZP_CLK
+23,PTA1,JTAG_TDI/EZP_DI,TSI0_CH2,PTA1,UART0_RX,FTM0_CH6,,,,JTAG_TDI,EZP_DI
+24,PTA2,JTAG_TDO/TRACE_SWO/EZP_DO,TSI0_CH3,PTA2,UART0_TX,FTM0_CH7,,,,JTAG_TDO/TRACE_SWO,EZP_DO
+25,PTA3,JTAG_TMS/SWD_DIO,TSI0_CH4,PTA3,UART0_RTS_b,FTM0_CH0,,,,JTAG_TMS/SWD_DIO,
+26,PTA4/LLWU_P3,NMI_b/EZP_CS_b,TSI0_CH5,PTA4/LLWU_P3,,FTM0_CH1,,,NMI_b,EZP_CS_b,
+27,PTA5,DISABLED,,PTA5,USB_CLKIN,FTM0_CH2,,CMP2_OUT,I2S0_TX_BCLK,JTAG_TRST_b,
+28,PTA12,CMP2_IN0,CMP2_IN0,PTA12,CAN0_TX,FTM1_CH0,,,I2S0_TXD0,FTM1_QD_PHA,
+29,PTA13/LLWU_P4,CMP2_IN1,CMP2_IN1,PTA13/LLWU_P4,CAN0_RX,FTM1_CH1,,,I2S0_TX_FS,FTM1_QD_PHB,
+30,VDD,VDD,VDD,,,,,,,,
+31,VSS,VSS,VSS,,,,,,,,
+32,PTA18,EXTAL0,EXTAL0,PTA18,,FTM0_FLT2,FTM_CLKIN0,,,,
+33,PTA19,XTAL0,XTAL0,PTA19,,FTM1_FLT0,FTM_CLKIN1,,LPTMR0_ALT1,,
+34,RESET_b,RESET_b,RESET_b,,,,,,,,
+35,PTB0/LLWU_P5,ADC0_SE8/ADC1_SE8/TSI0_CH0,ADC0_SE8/ADC1_SE8/TSI0_CH0,PTB0/LLWU_P5,I2C0_SCL,FTM1_CH0,,,FTM1_QD_PHA,,
+36,PTB1,ADC0_SE9/ADC1_SE9/TSI0_CH6,ADC0_SE9/ADC1_SE9/TSI0_CH6,PTB1,I2C0_SDA,FTM1_CH1,,,FTM1_QD_PHB,,
+37,PTB2,ADC0_SE12/TSI0_CH7,ADC0_SE12/TSI0_CH7,PTB2,I2C0_SCL,UART0_RTS_b,,,FTM0_FLT3,,
+38,PTB3,ADC0_SE13/TSI0_CH8,ADC0_SE13/TSI0_CH8,PTB3,I2C0_SDA,UART0_CTS_b/UART0_COL_b,,,FTM0_FLT0,,
+39,PTB16,TSI0_CH9,TSI0_CH9,PTB16,SPI1_SOUT,UART0_RX,,FB_AD17,EWM_IN,,
+40,PTB17,TSI0_CH10,TSI0_CH10,PTB17,SPI1_SIN,UART0_TX,,FB_AD16,EWM_OUT_b,,
+41,PTB18,TSI0_CH11,TSI0_CH11,PTB18,CAN0_TX,FTM2_CH0,I2S0_TX_BCLK,FB_AD15,FTM2_QD_PHA,,
+42,PTB19,TSI0_CH12,TSI0_CH12,PTB19,CAN0_RX,FTM2_CH1,I2S0_TX_FS,FB_OE_b,FTM2_QD_PHB,,
+43,PTC0,ADC0_SE14/TSI0_CH13,ADC0_SE14/TSI0_CH13,PTC0,SPI0_PCS4,PDB0_EXTRG,,FB_AD14,I2S0_TXD1,,
+44,PTC1/LLWU_P6,ADC0_SE15/TSI0_CH14,ADC0_SE15/TSI0_CH14,PTC1/LLWU_P6,SPI0_PCS3,UART1_RTS_b,FTM0_CH0,FB_AD13,I2S0_TXD0,,
+45,PTC2,ADC0_SE4b/CMP1_IN0/TSI0_CH15,ADC0_SE4b/CMP1_IN0/TSI0_CH15,PTC2,SPI0_PCS2,UART1_CTS_b,FTM0_CH1,FB_AD12,I2S0_TX_FS,,
+46,PTC3/LLWU_P7,CMP1_IN1,CMP1_IN1,PTC3/LLWU_P7,SPI0_PCS1,UART1_RX,FTM0_CH2,CLKOUT,I2S0_TX_BCLK,,
+47,VSS,VSS,VSS,,,,,,,,
+48,VDD,VDD,VDD,,,,,,,,
+49,PTC4/LLWU_P8,DISABLED,,PTC4/LLWU_P8,SPI0_PCS0,UART1_TX,FTM0_CH3,FB_AD11,CMP1_OUT,,
+50,PTC5/LLWU_P9,DISABLED,,PTC5/LLWU_P9,SPI0_SCK,LPTMR0_ALT2,I2S0_RXD0,FB_AD10,CMP0_OUT,,
+51,PTC6/LLWU_P10,CMP0_IN0,CMP0_IN0,PTC6/LLWU_P10,SPI0_SOUT,PDB0_EXTRG,I2S0_RX_BCLK,FB_AD9,I2S0_MCLK,,
+52,PTC7,CMP0_IN1,CMP0_IN1,PTC7,SPI0_SIN,USB_SOF_OUT,I2S0_RX_FS,FB_AD8,,,
+53,PTC8,ADC1_SE4b/CMP0_IN2,ADC1_SE4b/CMP0_IN2,PTC8,,,I2S0_MCLK,FB_AD7,,,
+54,PTC9,ADC1_SE5b/CMP0_IN3,ADC1_SE5b/CMP0_IN3,PTC9,,,I2S0_RX_BCLK,FB_AD6,FTM2_FLT0,,
+55,PTC10,ADC1_SE6b,ADC1_SE6b,PTC10,I2C1_SCL,,I2S0_RX_FS,FB_AD5,,,
+56,PTC11/LLWU_P11,ADC1_SE7b,ADC1_SE7b,PTC11/LLWU_P11,I2C1_SDA,,I2S0_RXD1,FB_RW_b,,,
+57,PTD0/LLWU_P12,DISABLED,,PTD0/LLWU_P12,SPI0_PCS0,UART2_RTS_b,,FB_ALE/FB_CS1_b/FB_TS_b,,,
+58,PTD1,ADC0_SE5b,ADC0_SE5b,PTD1,SPI0_SCK,UART2_CTS_b,,FB_CS0_b,,,
+59,PTD2/LLWU_P13,DISABLED,,PTD2/LLWU_P13,SPI0_SOUT,UART2_RX,,FB_AD4,,,
+60,PTD3,DISABLED,,PTD3,SPI0_SIN,UART2_TX,,FB_AD3,,,
+61,PTD4/LLWU_P14,DISABLED,,PTD4/LLWU_P14,SPI0_PCS1,UART0_RTS_b,FTM0_CH4,FB_AD2,EWM_IN,,
+62,PTD5,ADC0_SE6b,ADC0_SE6b,PTD5,SPI0_PCS2,UART0_CTS_b/UART0_COL_b,FTM0_CH5,FB_AD1,EWM_OUT_b,,
+63,PTD6/LLWU_P15,ADC0_SE7b,ADC0_SE7b,PTD6/LLWU_P15,SPI0_PCS3,UART0_RX,FTM0_CH6,FB_AD0,FTM0_FLT0,,
+64,PTD7,DISABLED,,PTD7,CMT_IRO,UART0_TX,FTM0_CH7,,FTM0_FLT1,,
diff --git a/teensy/mk20dx256-prefix.c b/teensy/mk20dx256-prefix.c
new file mode 100644
index 0000000000..ea9eec74a2
--- /dev/null
+++ b/teensy/mk20dx256-prefix.c
@@ -0,0 +1,37 @@
+// stm32fxx-prefix.c becomes the initial portion of the generated pins file.
+
+#include <stdio.h>
+#include <stdint.h>
+#include <mk20dx128.h>
+
+#include "teensy_hal.h"
+
+#include "misc.h"
+#include "mpconfig.h"
+#include "qstr.h"
+#include "obj.h"
+#include "pin.h"
+
+#define AF(af_idx, af_fn, af_unit, af_type, af_ptr) \
+{ \
+    { &pin_af_type }, \
+    .idx = (af_idx), \
+    .fn = AF_FN_ ## af_fn, \
+    .unit = (af_unit), \
+    .type = AF_PIN_TYPE_ ## af_fn ## _ ## af_type, \
+    .af_fn = (af_ptr) \
+}
+
+#define PIN(p_port, p_pin, p_num_af, p_af, p_adc_num, p_adc_channel) \
+{ \
+    { &pin_type }, \
+    .name = #p_port #p_pin, \
+    .port = PORT_ ## p_port, \
+    .pin = (p_pin), \
+    .num_af = (p_num_af), \
+    .pin_mask = (1 << (p_pin)), \
+    .gpio = GPIO ## p_port, \
+    .af = p_af, \
+    .adc_num = p_adc_num, \
+    .adc_channel = p_adc_channel, \
+}
diff --git a/teensy/mk20dx256.ld b/teensy/mk20dx256.ld
index e46efd3e26..bff0a8c4aa 100644
--- a/teensy/mk20dx256.ld
+++ b/teensy/mk20dx256.ld
@@ -10,10 +10,10 @@
  * permit persons to whom the Software is furnished to do so, subject to
  * the following conditions:
  *
- * 1. The above copyright notice and this permission notice shall be 
+ * 1. The above copyright notice and this permission notice shall be
  * included in all copies or substantial portions of the Software.
  *
- * 2. If the Software is incorporated into a build system that allows 
+ * 2. If the Software is incorporated into a build system that allows
  * selection among a list of target devices, then similar target
  * devices manufactured by PJRC.COM must be included in the list of
  * target devices and selectable in the same manner.
@@ -30,8 +30,8 @@
 
 MEMORY
 {
-	FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K
-	RAM  (rwx) : ORIGIN = 0x1FFF8000, LENGTH = 64K
+        FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K
+        RAM  (rwx) : ORIGIN = 0x1FFF8000, LENGTH = 64K
 }
 
 /* produce a link error if there is not this amount of RAM for these sections */
@@ -52,10 +52,10 @@ _minimum_heap_size = 16K;
  * permit persons to whom the Software is furnished to do so, subject to
  * the following conditions:
  *
- * 1. The above copyright notice and this permission notice shall be 
+ * 1. The above copyright notice and this permission notice shall be
  * included in all copies or substantial portions of the Software.
  *
- * 2. If the Software is incorporated into a build system that allows 
+ * 2. If the Software is incorporated into a build system that allows
  * selection among a list of target devices, then similar target
  * devices manufactured by PJRC.COM must be included in the list of
  * target devices and selectable in the same manner.
@@ -74,95 +74,101 @@ _minimum_heap_size = 16K;
 
 SECTIONS
 {
-	.text : {
-		. = 0;
-		KEEP(*(.vectors))
-		*(.startup*)
-		/* TODO: does linker detect startup overflow onto flashconfig? */
-		. = 0x400;
-		KEEP(*(.flashconfig*))
-		*(.text*)
-		*(.rodata*)
-		. = ALIGN(4);
-		KEEP(*(.init))
-		. = ALIGN(4);
-		__preinit_array_start = .;
-		KEEP (*(.preinit_array))
-		__preinit_array_end = .;
-		__init_array_start = .;
-		KEEP (*(SORT(.init_array.*)))
-		KEEP (*(.init_array))
-		__init_array_end = .;
-	} > FLASH = 0xFF
+    .text : {
+        . = 0;
+        KEEP(*(.vectors))
+        *(.startup*)
+        /* TODO: does linker detect startup overflow onto flashconfig? */
+        . = 0x400;
+        KEEP(*(.flashconfig*))
+        *(.text*)
+        *(.rodata*)
+        . = ALIGN(4);
+        KEEP(*(.init))
+        . = ALIGN(4);
+        __preinit_array_start = .;
+        KEEP (*(.preinit_array))
+        __preinit_array_end = .;
+        __init_array_start = .;
+        KEEP (*(SORT(.init_array.*)))
+        KEEP (*(.init_array))
+        __init_array_end = .;
+    } > FLASH = 0xFF
 
-	.ARM.exidx : {
-		__exidx_start = .;
-		*(.ARM.exidx* .gnu.linkonce.armexidx.*)
-		__exidx_end = .;
-	} > FLASH
-	_etext = .;
+    .ARM.exidx : {
+        __exidx_start = .;
+        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+        __exidx_end = .;
+    } > FLASH
+    _etext = .;
 
-	.usbdescriptortable (NOLOAD) : {
-		/* . = ORIGIN(RAM); */
-		. = ALIGN(512);
-		*(.usbdescriptortable*)
-	} > RAM
+    .usbdescriptortable (NOLOAD) : {
+        /* . = ORIGIN(RAM); */
+        . = ALIGN(512);
+        *(.usbdescriptortable*)
+    } > RAM
 
-	.dmabuffers (NOLOAD) : {
-		. = ALIGN(4);
-		*(.dmabuffers*)
-	} > RAM
+    .dmabuffers (NOLOAD) : {
+        . = ALIGN(4);
+        *(.dmabuffers*)
+    } > RAM
 
-	.usbbuffers (NOLOAD) : {
-		. = ALIGN(4);
-		*(.usbbuffers*)
-	} > RAM
+    .usbbuffers (NOLOAD) : {
+        . = ALIGN(4);
+        *(.usbbuffers*)
+    } > RAM
 
-	.data : AT (_etext) {
-		. = ALIGN(4);
-		_sdata = .; 
-		*(.data*)
-		. = ALIGN(4);
-		_edata = .; 
-	} > RAM
+    /* used by the startup to initialize data */
+    _sidata = LOADADDR(.data);
 
-	/*
-	 * _staticfs is the place in flash where the static filesystem which
-	 * is concatenated to the .hex file will wind up.
-	 */
-	_staticfs = LOADADDR(.data) + SIZEOF(.data);
+    .data : AT (_etext) {
+        . = ALIGN(4);
+        _sdata = .;
+        _ram_start = .;
+        *(.data*)
+        . = ALIGN(4);
+        _edata = .;
+    } > RAM
 
-	.noinit (NOLOAD) : {
-		*(.noinit*)
-	} > RAM
+        /*
+     * _staticfs is the place in flash where the static filesystem which
+     * is concatenated to the .hex file will wind up.
+         */
+    _staticfs = LOADADDR(.data) + SIZEOF(.data);
 
-	.bss : {
-		. = ALIGN(4);
-		_sbss = .; 
-		*(.bss*)
-		*(COMMON)
-		. = ALIGN(4);
-		_ebss = .;
-		__bss_end = .;
-	} > RAM
+    .noinit (NOLOAD) : {
+        *(.noinit*)
+    } > RAM
 
-	/* this is to define the start of the heap, and make sure we have a minimum size */
-	.heap :
-	{
-	    . = ALIGN(4);
-	    _heap_start = .;    /* define a global symbol at heap start */
-	    . = . + _minimum_heap_size;
-	} >RAM
+    .bss : {
+        . = ALIGN(4);
+        _sbss = .;
+        *(.bss*)
+        *(COMMON)
+        . = ALIGN(4);
+        _ebss = .;
+        __bss_end = .;
+    } > RAM
 
-	/* this just checks there is enough RAM for the stack */
-	.stack :
-	{
-	    . = ALIGN(4);
-	    . = . + _minimum_stack_size;
-	    . = ALIGN(4);
-	} >RAM
+    /* this is to define the start of the heap, and make sure we have a minimum size */
+    .heap :
+        {
+        . = ALIGN(4);
+        _heap_start = .;    /* define a global symbol at heap start */
+        . = . + _minimum_heap_size;
+    } >RAM
 
-	_estack = ORIGIN(RAM) + LENGTH(RAM);
+    /* this just checks there is enough RAM for the stack */
+    .stack :
+        {
+        . = ALIGN(4);
+        . = . + _minimum_stack_size;
+        . = ALIGN(4);
+    } >RAM
+
+    _estack = ORIGIN(RAM) + LENGTH(RAM);
+    _ram_end = ORIGIN(RAM) + LENGTH(RAM);
+    _heap_end = ORIGIN(RAM) + 0xe000;
 }
 
 
diff --git a/teensy/modpyb.c b/teensy/modpyb.c
new file mode 100644
index 0000000000..0240395dc7
--- /dev/null
+++ b/teensy/modpyb.c
@@ -0,0 +1,341 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 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 <stdint.h>
+#include <stdio.h>
+#include <mk20dx128.h>
+#include "Arduino.h"
+
+#include "misc.h"
+#include "mpconfig.h"
+
+#include "teensy_hal.h"
+
+#include "qstr.h"
+#include "obj.h"
+#include "gc.h"
+#include "gccollect.h"
+#include "systick.h"
+#include "pybstdio.h"
+#include "pyexec.h"
+#include "led.h"
+#include "pin.h"
+//#include "timer.h"
+#include "extint.h"
+#include "usrsw.h"
+#include "rng.h"
+//#include "rtc.h"
+//#include "i2c.h"
+//#include "spi.h"
+#include "uart.h"
+#include "adc.h"
+#include "storage.h"
+#include "sdcard.h"
+#include "accel.h"
+#include "servo.h"
+#include "dac.h"
+#include "usb.h"
+#include "portmodules.h"
+
+/// \module pyb - functions related to the pyboard
+///
+/// The `pyb` module contains specific functions related to the pyboard.
+
+/// \function bootloader()
+/// Activate the bootloader without BOOT* pins.
+STATIC mp_obj_t pyb_bootloader(void) {
+    printf("bootloader command not current supported\n");
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_bootloader_obj, pyb_bootloader);
+
+/// \function info([dump_alloc_table])
+/// Print out lots of information about the board.
+STATIC mp_obj_t pyb_info(uint n_args, const mp_obj_t *args) {
+    // get and print unique id; 96 bits
+    {
+        byte *id = (byte*)0x40048058;
+        printf("ID=%02x%02x%02x%02x:%02x%02x%02x%02x:%02x%02x%02x%02x\n", id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10], id[11]);
+    }
+
+    // get and print clock speeds
+    printf("CPU=%u\nBUS=%u\nMEM=%u\n", F_CPU, F_BUS, F_MEM);
+
+    // to print info about memory
+    {
+        printf("_etext=%p\n", &_etext);
+        printf("_sidata=%p\n", &_sidata);
+        printf("_sdata=%p\n", &_sdata);
+        printf("_edata=%p\n", &_edata);
+        printf("_sbss=%p\n", &_sbss);
+        printf("_ebss=%p\n", &_ebss);
+        printf("_estack=%p\n", &_estack);
+        printf("_ram_start=%p\n", &_ram_start);
+        printf("_heap_start=%p\n", &_heap_start);
+        printf("_heap_end=%p\n", &_heap_end);
+        printf("_ram_end=%p\n", &_ram_end);
+    }
+
+    // qstr info
+    {
+        uint n_pool, n_qstr, n_str_data_bytes, n_total_bytes;
+        qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes);
+        printf("qstr:\n  n_pool=%u\n  n_qstr=%u\n  n_str_data_bytes=%u\n  n_total_bytes=%u\n", n_pool, n_qstr, n_str_data_bytes, n_total_bytes);
+    }
+
+    // GC info
+    {
+        gc_info_t info;
+        gc_info(&info);
+        printf("GC:\n");
+        printf("  " UINT_FMT " total\n", info.total);
+        printf("  " UINT_FMT " : " UINT_FMT "\n", info.used, info.free);
+        printf("  1=" UINT_FMT " 2=" UINT_FMT " m=" UINT_FMT "\n", info.num_1block, info.num_2block, info.max_block);
+    }
+
+    if (n_args == 1) {
+        // arg given means dump gc allocation table
+        gc_dump_alloc_table();
+    }
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_info_obj, 0, 1, pyb_info);
+
+/// \function unique_id()
+/// Returns a string of 12 bytes (96 bits), which is the unique ID for the MCU.
+STATIC mp_obj_t pyb_unique_id(void) {
+    byte *id = (byte*)0x40048058;
+    return mp_obj_new_bytes(id, 12);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_unique_id_obj, pyb_unique_id);
+
+/// \function freq()
+/// Return a tuple of clock frequencies: (SYSCLK, HCLK, PCLK1, PCLK2).
+// TODO should also be able to set frequency via this function
+STATIC mp_obj_t pyb_freq(void) {
+    mp_obj_t tuple[3] = {
+       mp_obj_new_int(F_CPU),
+       mp_obj_new_int(F_BUS),
+       mp_obj_new_int(F_MEM),
+    };
+    return mp_obj_new_tuple(3, tuple);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_freq_obj, pyb_freq);
+
+/// \function sync()
+/// Sync all file systems.
+STATIC mp_obj_t pyb_sync(void) {
+    printf("sync not currently implemented\n");
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_sync_obj, pyb_sync);
+
+/// \function millis()
+/// Returns the number of milliseconds since the board was last reset.
+STATIC mp_obj_t pyb_millis(void) {
+    return mp_obj_new_int(HAL_GetTick());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_millis_obj, pyb_millis);
+
+/// \function delay(ms)
+/// Delay for the given number of milliseconds.
+STATIC mp_obj_t pyb_delay(mp_obj_t ms_in) {
+    machine_int_t ms = mp_obj_get_int(ms_in);
+    if (ms >= 0) {
+        HAL_Delay(ms);
+    }
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_delay_obj, pyb_delay);
+
+/// \function udelay(us)
+/// Delay for the given number of microseconds.
+STATIC mp_obj_t pyb_udelay(mp_obj_t usec_in) {
+    machine_int_t usec = mp_obj_get_int(usec_in);
+    delayMicroseconds(usec);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_udelay_obj, pyb_udelay);
+
+/// \function wfi()
+/// Wait for an interrupt.
+/// This executies a `wfi` instruction which reduces power consumption
+/// of the MCU until an interrupt occurs, at which point execution continues.
+STATIC mp_obj_t pyb_wfi(void) {
+    __WFI();
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_0(pyb_wfi_obj, pyb_wfi);
+
+/// \function disable_irq()
+/// Disable interrupt requests.
+STATIC mp_obj_t pyb_disable_irq(void) {
+    __disable_irq();
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_0(pyb_disable_irq_obj, pyb_disable_irq);
+
+/// \function enable_irq()
+/// Enable interrupt requests.
+STATIC mp_obj_t pyb_enable_irq(void) {
+    __enable_irq();
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_0(pyb_enable_irq_obj, pyb_enable_irq);
+
+STATIC mp_obj_t pyb_stop(void) {
+    printf("stop not currently implemented\n");
+    return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_0(pyb_stop_obj, pyb_stop);
+
+STATIC mp_obj_t pyb_standby(void) {
+    printf("standby not currently implemented\n");
+    return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_0(pyb_standby_obj, pyb_standby);
+
+/// \function have_cdc()
+/// Return True if USB is connected as a serial device, False otherwise.
+STATIC mp_obj_t pyb_have_cdc(void ) {
+    return MP_BOOL(usb_vcp_is_connected());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(pyb_have_cdc_obj, pyb_have_cdc);
+
+/// \function hid((buttons, x, y, z))
+/// Takes a 4-tuple (or list) and sends it to the USB host (the PC) to
+/// signal a HID mouse-motion event.
+STATIC mp_obj_t pyb_hid_send_report(mp_obj_t arg) {
+#if 1
+    printf("hid_send_report not currently implemented\n");
+#else
+    mp_obj_t *items;
+    mp_obj_get_array_fixed_n(arg, 4, &items);
+    uint8_t data[4];
+    data[0] = mp_obj_get_int(items[0]);
+    data[1] = mp_obj_get_int(items[1]);
+    data[2] = mp_obj_get_int(items[2]);
+    data[3] = mp_obj_get_int(items[3]);
+    usb_hid_send_report(data);
+#endif
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_hid_send_report_obj, pyb_hid_send_report);
+
+MP_DECLARE_CONST_FUN_OBJ(pyb_source_dir_obj); // defined in main.c
+MP_DECLARE_CONST_FUN_OBJ(pyb_main_obj); // defined in main.c
+MP_DECLARE_CONST_FUN_OBJ(pyb_usb_mode_obj); // defined in main.c
+
+STATIC const mp_map_elem_t pyb_module_globals_table[] = {
+    { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_pyb) },
+
+    { MP_OBJ_NEW_QSTR(MP_QSTR_bootloader), (mp_obj_t)&pyb_bootloader_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_info), (mp_obj_t)&pyb_info_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_unique_id), (mp_obj_t)&pyb_unique_id_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_freq), (mp_obj_t)&pyb_freq_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_repl_info), (mp_obj_t)&pyb_set_repl_info_obj },
+
+    { MP_OBJ_NEW_QSTR(MP_QSTR_wfi), (mp_obj_t)&pyb_wfi_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_disable_irq), (mp_obj_t)&pyb_disable_irq_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_enable_irq), (mp_obj_t)&pyb_enable_irq_obj },
+
+    { MP_OBJ_NEW_QSTR(MP_QSTR_stop), (mp_obj_t)&pyb_stop_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_standby), (mp_obj_t)&pyb_standby_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_source_dir), (mp_obj_t)&pyb_source_dir_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_main), (mp_obj_t)&pyb_main_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_usb_mode), (mp_obj_t)&pyb_usb_mode_obj },
+
+    { MP_OBJ_NEW_QSTR(MP_QSTR_have_cdc), (mp_obj_t)&pyb_have_cdc_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_hid), (mp_obj_t)&pyb_hid_send_report_obj },
+
+    { MP_OBJ_NEW_QSTR(MP_QSTR_millis), (mp_obj_t)&pyb_millis_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_delay), (mp_obj_t)&pyb_delay_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_udelay), (mp_obj_t)&pyb_udelay_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_sync), (mp_obj_t)&pyb_sync_obj },
+
+//    { MP_OBJ_NEW_QSTR(MP_QSTR_Timer), (mp_obj_t)&pyb_timer_type },
+
+//#if MICROPY_HW_ENABLE_RNG
+//    { MP_OBJ_NEW_QSTR(MP_QSTR_rng), (mp_obj_t)&pyb_rng_get_obj },
+//#endif
+
+//#if MICROPY_HW_ENABLE_RTC
+//    { MP_OBJ_NEW_QSTR(MP_QSTR_RTC), (mp_obj_t)&pyb_rtc_type },
+//#endif
+
+    { MP_OBJ_NEW_QSTR(MP_QSTR_Pin), (mp_obj_t)&pin_type },
+//    { MP_OBJ_NEW_QSTR(MP_QSTR_ExtInt), (mp_obj_t)&extint_type },
+
+#if MICROPY_HW_ENABLE_SERVO
+    { MP_OBJ_NEW_QSTR(MP_QSTR_pwm), (mp_obj_t)&pyb_pwm_set_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_servo), (mp_obj_t)&pyb_servo_set_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_Servo), (mp_obj_t)&pyb_servo_type },
+#endif
+
+#if MICROPY_HW_HAS_SWITCH
+    { MP_OBJ_NEW_QSTR(MP_QSTR_Switch), (mp_obj_t)&pyb_switch_type },
+#endif
+
+//#if MICROPY_HW_HAS_SDCARD
+//    { MP_OBJ_NEW_QSTR(MP_QSTR_SD), (mp_obj_t)&pyb_sdcard_obj },
+//#endif
+
+    { MP_OBJ_NEW_QSTR(MP_QSTR_LED), (mp_obj_t)&pyb_led_type },
+//    { MP_OBJ_NEW_QSTR(MP_QSTR_I2C), (mp_obj_t)&pyb_i2c_type },
+//    { MP_OBJ_NEW_QSTR(MP_QSTR_SPI), (mp_obj_t)&pyb_spi_type },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_UART), (mp_obj_t)&pyb_uart_type },
+
+//    { MP_OBJ_NEW_QSTR(MP_QSTR_ADC), (mp_obj_t)&pyb_adc_type },
+//    { MP_OBJ_NEW_QSTR(MP_QSTR_ADCAll), (mp_obj_t)&pyb_adc_all_type },
+
+//#if MICROPY_HW_ENABLE_DAC
+//    { MP_OBJ_NEW_QSTR(MP_QSTR_DAC), (mp_obj_t)&pyb_dac_type },
+//#endif
+
+//#if MICROPY_HW_HAS_MMA7660
+//    { MP_OBJ_NEW_QSTR(MP_QSTR_Accel), (mp_obj_t)&pyb_accel_type },
+//#endif
+};
+
+STATIC const mp_obj_dict_t pyb_module_globals = {
+    .base = {&mp_type_dict},
+    .map = {
+        .all_keys_are_qstrs = 1,
+        .table_is_fixed_array = 1,
+        .used = ARRAY_SIZE(pyb_module_globals_table),
+        .alloc = ARRAY_SIZE(pyb_module_globals_table),
+        .table = (mp_map_elem_t*)pyb_module_globals_table,
+    },
+};
+
+const mp_obj_module_t pyb_module = {
+    .base = { &mp_type_module },
+    .name = MP_QSTR_pyb,
+    .globals = (mp_obj_dict_t*)&pyb_module_globals,
+};
diff --git a/teensy/mpconfigport.h b/teensy/mpconfigport.h
index f7c66d8874..417335cfcb 100644
--- a/teensy/mpconfigport.h
+++ b/teensy/mpconfigport.h
@@ -2,20 +2,102 @@
 
 // options to control how Micro Python is built
 
+#define MICROPY_ALLOC_PATH_MAX      (128)
 #define MICROPY_EMIT_THUMB          (1)
 #define MICROPY_EMIT_INLINE_THUMB   (1)
 #define MICROPY_ENABLE_GC           (1)
+#define MICROPY_ENABLE_FINALISER    (1)
 #define MICROPY_HELPER_REPL         (1)
 #define MICROPY_PY_BUILTINS_FLOAT   (1)
+#define MICROPY_ENABLE_SOURCE_LINE  (1)
+#define MICROPY_LONGINT_IMPL        (MICROPY_LONGINT_IMPL_MPZ)
+#define MICROPY_FLOAT_IMPL          (MICROPY_FLOAT_IMPL_FLOAT)
+#define MICROPY_OPT_COMPUTED_GOTO   (1)
+
+#define MICROPY_PY_IO               (0)
+#define MICROPY_PY_FROZENSET        (1)
+#define MICROPY_PY_SYS_EXIT         (1)
+#define MICROPY_PY_SYS_STDFILES     (1)
+#define MICROPY_PY_CMATH            (1)
+
+// extra built in names to add to the global namespace
+extern const struct _mp_obj_fun_native_t mp_builtin_help_obj;
+extern const struct _mp_obj_fun_native_t mp_builtin_input_obj;
+extern const struct _mp_obj_fun_native_t mp_builtin_open_obj;
+#define MICROPY_PORT_BUILTINS \
+    { MP_OBJ_NEW_QSTR(MP_QSTR_help), (mp_obj_t)&mp_builtin_help_obj }, \
+    { MP_OBJ_NEW_QSTR(MP_QSTR_input), (mp_obj_t)&mp_builtin_input_obj }, \
+
+// extra built in modules to add to the list of known ones
+extern const struct _mp_obj_module_t os_module;
+extern const struct _mp_obj_module_t pyb_module;
+extern const struct _mp_obj_module_t time_module;
+#define MICROPY_PORT_BUILTIN_MODULES \
+    { MP_OBJ_NEW_QSTR(MP_QSTR_pyb), (mp_obj_t)&pyb_module }, \
+
+// extra constants
+#define MICROPY_PORT_CONSTANTS \
+    { MP_OBJ_NEW_QSTR(MP_QSTR_pyb), (mp_obj_t)&pyb_module }, \
 
 // type definitions for the specific machine
 
 #define BYTES_PER_WORD (4)
 
+#define UINT_FMT "%u"
+#define INT_FMT "%d"
+
 typedef int32_t machine_int_t; // must be pointer size
-typedef uint32_t machine_uint_t; // must be pointer size
+typedef unsigned int machine_uint_t; // must be pointer size
 typedef void *machine_ptr_t; // must be of pointer size
 typedef const void *machine_const_ptr_t; // must be of pointer size
-typedef float machine_float_t;
 
-machine_float_t machine_sqrt(machine_float_t x);
+// There is no classical C heap in bare-metal ports, only Python
+// garbage-collected heap. For completeness, emulate C heap via
+// GC heap. Note that MicroPython core never uses malloc() and friends,
+// so these defines are mostly to help extension module writers.
+#define malloc gc_alloc
+#define free gc_free
+#define realloc gc_realloc
+
+// We need to provide a declaration/definition of alloca()
+#include <alloca.h>
+
+// The following would be from a board specific file, if one existed
+
+#define MICROPY_HW_BOARD_NAME       "Teensy-3.1"
+
+#define MICROPY_HW_HAS_SWITCH       (0)
+#define MICROPY_HW_HAS_SDCARD       (0)
+#define MICROPY_HW_HAS_MMA7660      (0)
+#define MICROPY_HW_HAS_LIS3DSH      (0)
+#define MICROPY_HW_HAS_LCD          (0)
+#define MICROPY_HW_ENABLE_RNG       (0)
+#define MICROPY_HW_ENABLE_RTC       (0)
+#define MICROPY_HW_ENABLE_TIMER     (0)
+#define MICROPY_HW_ENABLE_SERVO     (0)
+#define MICROPY_HW_ENABLE_DAC       (0)
+#define MICROPY_HW_ENABLE_I2C1      (0)
+#define MICROPY_HW_ENABLE_SPI1      (0)
+#define MICROPY_HW_ENABLE_SPI3      (0)
+#define MICROPY_HW_ENABLE_CC3K      (0)
+
+#define MICROPY_HW_LED1             (pin_C5)
+#define MICROPY_HW_LED_OTYPE        (GPIO_MODE_OUTPUT_PP)
+#define MICROPY_HW_LED_ON(pin)      (pin->gpio->PSOR = pin->pin_mask)
+#define MICROPY_HW_LED_OFF(pin)     (pin->gpio->PCOR = pin->pin_mask)
+
+#if 0
+// SD card detect switch
+#define MICROPY_HW_SDCARD_DETECT_PIN        (pin_A8)
+#define MICROPY_HW_SDCARD_DETECT_PULL       (GPIO_PULLUP)
+#define MICROPY_HW_SDCARD_DETECT_PRESENT    (GPIO_PIN_RESET)
+#endif
+
+#define MICROPY_MATH_SQRT_ASM     (1)
+
+#define HAL_H           "teensy_hal.h"
+#define PIN_DEFS_PORT_H "pin_defs_teensy.h"
+
+#define GPIO_read_pin(gpio, pin)        (((gpio)->PDIR >> (pin)) & 1)
+#define GPIO_set_pin(gpio, pin_mask)    (((gpio)->PSOR) = (pin_mask))
+#define GPIO_clear_pin(gpio, pin_mask)  (((gpio)->PCOR) = (pin_mask))
diff --git a/teensy/pin_defs_teensy.h b/teensy/pin_defs_teensy.h
new file mode 100644
index 0000000000..66942c2ea8
--- /dev/null
+++ b/teensy/pin_defs_teensy.h
@@ -0,0 +1,46 @@
+enum {
+  PORT_A,
+  PORT_B,
+  PORT_C,
+  PORT_D,
+  PORT_E,
+  PORT_Z,
+};
+
+enum {
+    AF_FN_FTM,
+    AF_FN_I2C,
+    AF_FN_UART,
+    AF_FN_SPI
+};
+
+enum {
+    AF_PIN_TYPE_FTM_CH0 = 0,
+    AF_PIN_TYPE_FTM_CH1,
+    AF_PIN_TYPE_FTM_CH2,
+    AF_PIN_TYPE_FTM_CH3,
+    AF_PIN_TYPE_FTM_QD_PHA,
+    AF_PIN_TYPE_FTM_QD_PHB,
+
+    AF_PIN_TYPE_I2C_SDA = 0,
+    AF_PIN_TYPE_I2C_SCL,
+
+    AF_PIN_TYPE_SPI_MOSI = 0,
+    AF_PIN_TYPE_SPI_MISO,
+    AF_PIN_TYPE_SPI_SCK,
+    AF_PIN_TYPE_SPI_NSS,
+
+    AF_PIN_TYPE_UART_TX = 0,
+    AF_PIN_TYPE_UART_RX,
+    AF_PIN_TYPE_UART_CTS,
+    AF_PIN_TYPE_UART_RTS,
+};
+
+#define PIN_DEFS_PORT_AF_UNION \
+    FTM_TypeDef   *FTM; \
+    I2C_TypeDef   *I2C; \
+    UART_TypeDef  *UART; \
+    SPI_TypeDef   *SPI;
+
+typedef GPIO_TypeDef pin_gpio_t;
+
diff --git a/teensy/qstrdefsport.h b/teensy/qstrdefsport.h
index 8b23a86bf0..2fb70af7b4 100644
--- a/teensy/qstrdefsport.h
+++ b/teensy/qstrdefsport.h
@@ -20,7 +20,7 @@ Q(mma_mode)
 Q(hid)
 Q(time)
 Q(rand)
-Q(Led)
+Q(LED)
 Q(led)
 Q(Servo)
 Q(I2C)
@@ -32,5 +32,63 @@ Q(analogRead)
 Q(analogWrite)
 Q(analogWriteResolution)
 Q(analogWriteFrequency)
-Q(run)
+Q(on)
+Q(off)
+Q(toggle)
+Q(readall)
+Q(readline)
+Q(FileIO)
+Q(input)
+Q(os)
+Q(bootloader)
+Q(unique_id)
+Q(freq)
+Q(repl_info)
+Q(wfi)
+Q(disable_irq)
+Q(enable_irq)
+Q(usb_mode)
+Q(have_cdc)
+Q(millis)
+Q(udelay)
+Q(UART)
+
+// for Pin class
+Q(Pin)
+Q(PinAF)
+Q(PinNamed)
+Q(init)
+Q(value)
+Q(low)
+Q(high)
+Q(name)
+Q(port)
+Q(pin)
+Q(mapper)
+Q(dict)
+Q(debug)
+Q(board)
+Q(cpu)
+Q(IN)
+Q(OUT_PP)
+Q(OUT_OD)
+Q(AF_PP)
+Q(AF_OD)
+Q(ANALOG)
+Q(PULL_NONE)
+Q(PULL_UP)
+Q(PULL_DOWN)
+
+// for UART class
+Q(UART)
+Q(baudrate)
+Q(bits)
+Q(stop)
+Q(parity)
+Q(init)
+Q(deinit)
+Q(all)
+Q(send)
+Q(recv)
+Q(timeout)
 
diff --git a/teensy/servo.h b/teensy/servo.h
index c4a5bd49b1..5dad041138 100644
--- a/teensy/servo.h
+++ b/teensy/servo.h
@@ -1,2 +1,7 @@
-mp_obj_t pyb_Servo(void);
+void servo_init(void);
+
+extern const mp_obj_type_t pyb_servo_type;
+
+MP_DECLARE_CONST_FUN_OBJ(pyb_servo_set_obj);
+MP_DECLARE_CONST_FUN_OBJ(pyb_pwm_set_obj);
 
diff --git a/teensy/teensy-pins.csv b/teensy/teensy-pins.csv
new file mode 100644
index 0000000000..acaef63aad
--- /dev/null
+++ b/teensy/teensy-pins.csv
@@ -0,0 +1,55 @@
+D0,PTB16
+D1,PTB17
+D2,PTD0
+D3,PTA12
+D4,PTA13
+D5,PTD7
+D6,PTD4
+D7,PTD2
+D8,PTD3
+D9,PTC3
+D10,PTC4
+D11,PTC6
+D12,PTC7
+D13,PTC5
+D14,PTD1
+D15,PTC0
+D16,PTB0
+D17,PTB1
+D18,PTB3
+D19,PTB2
+D20,PTD5
+D21,PTD6
+D22,PTC1
+D23,PTC2
+D24,PTA5
+D25,PTB19
+D26,PTE1
+D27,PTC9
+D28,PTC8
+D29,PTC10
+D30,PTC11
+D31,PTE0
+D32,PTB18
+D33,PTA4
+A0,PTD1
+A1,PTC0
+A2,PTB0
+A3,PTB1
+A4,PTB3
+A5,PTB2
+A6,PTD5
+A7,PTD6
+A8,PTC1
+A9,PTC2
+A10,PTZ0
+A11,PTZ1
+A12,PTZ2
+A13,PTZ3
+A14,PTZ5
+A15,PTE1
+A16,PTC9
+A17,PTC8
+A18,PTC10
+A19,PTC11
+A20,PTE0
diff --git a/teensy/teensy_hal.c b/teensy/teensy_hal.c
new file mode 100644
index 0000000000..5f162f1e78
--- /dev/null
+++ b/teensy/teensy_hal.c
@@ -0,0 +1,16 @@
+#include <stdio.h>
+#include <stdint.h>
+
+#include "mpconfig.h"
+
+#include "Arduino.h"
+
+#include HAL_H
+
+uint32_t HAL_GetTick(void) {
+  return micros();
+}
+
+void HAL_Delay(uint32_t Delay) {
+  delay(Delay);
+}
diff --git a/teensy/teensy_hal.h b/teensy/teensy_hal.h
new file mode 100644
index 0000000000..3afe01b1c9
--- /dev/null
+++ b/teensy/teensy_hal.h
@@ -0,0 +1,108 @@
+
+#ifdef  USE_FULL_ASSERT
+  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
+  void assert_failed(uint8_t* file, uint32_t line);
+#else
+  #define assert_param(expr) ((void)0)
+#endif /* USE_FULL_ASSERT */    
+
+#define FTM0    ((FTM_TypeDef *)&FTM0_SC)
+#define FTM1    ((FTM_TypeDef *)&FTM1_SC)
+#define FTM2    ((FTM_TypeDef *)&FTM2_SC)
+
+#define GPIOA   ((GPIO_TypeDef *)&GPIOA_PDOR)
+#define GPIOB   ((GPIO_TypeDef *)&GPIOB_PDOR)
+#define GPIOC   ((GPIO_TypeDef *)&GPIOC_PDOR)
+#define GPIOD   ((GPIO_TypeDef *)&GPIOD_PDOR)
+#define GPIOE   ((GPIO_TypeDef *)&GPIOE_PDOR)
+#define GPIOZ   ((GPIO_TypeDef *)NULL)
+
+#define I2C0    ((I2C_TypeDef *)0x40066000)
+#define I2C1    ((I2C_TypeDef *)0x40067000)
+
+#undef  SPI0
+#define SPI0    ((SPI_TypeDef *)0x4002C000)
+#define SPI1    ((SPI_TypeDef *)0x4002D000)
+
+#define UART0   ((UART_TypeDef *)&UART0_BDH)
+#define UART1   ((UART_TypeDef *)&UART1_BDH)
+#define UART2   ((UART_TypeDef *)&UART2_BDH)
+
+typedef struct {
+    uint32_t dummy;
+} FTM_TypeDef;
+
+typedef struct {
+    uint32_t dummy;
+} I2C_TypeDef;
+
+typedef struct {
+    uint32_t dummy;
+} UART_TypeDef;
+
+typedef struct {
+    uint32_t dummy;
+} SPI_TypeDef;
+
+typedef struct {
+    volatile    uint32_t    PDOR;   // Output register
+    volatile    uint32_t    PSOR;   // Set output register
+    volatile    uint32_t    PCOR;   // Clear output register
+    volatile    uint32_t    PTOR;   // Toggle output register
+    volatile    uint32_t    PDIR;   // Data Input register
+    volatile    uint32_t    PDDR;   // Data Direction register
+} GPIO_TypeDef;
+
+#define GPIO_OUTPUT_TYPE    ((uint32_t)0x00000010)  // Indicates OD
+
+#define GPIO_MODE_INPUT     ((uint32_t)0x00000000)
+#define GPIO_MODE_OUTPUT_PP ((uint32_t)0x00000001)
+#define GPIO_MODE_OUTPUT_OD ((uint32_t)0x00000011)
+#define GPIO_MODE_AF_PP     ((uint32_t)0x00000002)
+#define GPIO_MODE_AF_OD     ((uint32_t)0x00000012)
+#define GPIO_MODE_ANALOG    ((uint32_t)0x00000003)
+
+#define IS_GPIO_MODE(MODE) (((MODE) == GPIO_MODE_INPUT)              ||\
+                            ((MODE) == GPIO_MODE_OUTPUT_PP)          ||\
+                            ((MODE) == GPIO_MODE_OUTPUT_OD)          ||\
+                            ((MODE) == GPIO_MODE_AF_PP)              ||\
+                            ((MODE) == GPIO_MODE_AF_OD)              ||\
+                            ((MODE) == GPIO_MODE_ANALOG))
+
+#define GPIO_NOPULL         ((uint32_t)0)
+#define GPIO_PULLUP         ((uint32_t)1)
+#define GPIO_PULLDOWN       ((uint32_t)2)
+
+#define IS_GPIO_PULL(PULL) (((PULL) == GPIO_NOPULL) || ((PULL) == GPIO_PULLUP) || \
+                            ((PULL) == GPIO_PULLDOWN))
+
+#define  GPIO_SPEED_LOW         ((uint32_t)0)
+#define  GPIO_SPEED_MEDIUM      ((uint32_t)1)
+#define  GPIO_SPEED_FAST        ((uint32_t)2)
+#define  GPIO_SPEED_HIGH        ((uint32_t)3)
+
+
+typedef struct {
+    uint32_t    Pin;
+    uint32_t    Mode;
+    uint32_t    Pull;
+    uint32_t    Speed;
+    uint32_t    Alternate;
+} GPIO_InitTypeDef;
+
+#define GPIO_PORT_TO_PORT_NUM(GPIOx) \
+    ((GPIOx->PDOR - GPIOA_PDOR) / (GPIOB_PDOR - GPIOA_PDOR))
+
+#define GPIO_PIN_TO_PORT_PCR(GPIOx, pin) \
+    (&PORTA_PCR0 + GPIO_PORT_TO_PORT_NUM(GPIOx) * 32 + (pin))
+
+__attribute__(( always_inline )) static inline void __WFI(void)
+{
+  __asm volatile ("wfi");
+}
+
+uint32_t HAL_GetTick(void);
+void     HAL_Delay(uint32_t Delay);
+
+void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *init);
+
diff --git a/teensy/uart.c b/teensy/uart.c
new file mode 100644
index 0000000000..3dd546dd54
--- /dev/null
+++ b/teensy/uart.c
@@ -0,0 +1,524 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, 2014 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 <stdio.h>
+#include <string.h>
+
+#include "mpconfig.h"
+
+#include HAL_H
+
+#include "nlr.h"
+#include "misc.h"
+#include "qstr.h"
+#include "obj.h"
+#include "runtime.h"
+#include "bufhelper.h"
+#include "uart.h"
+
+/// \moduleref pyb
+/// \class UART - duplex serial communication bus
+///
+/// UART implements the standard UART/USART duplex serial communications protocol.  At
+/// the physical level it consists of 2 lines: RX and TX.
+///
+/// See usage model of I2C.  UART is very similar.  Main difference is
+/// parameters to init the UART bus:
+///
+///     from pyb import UART
+///
+///     uart = UART(1, 9600)                         # init with given baudrate
+///     uart.init(9600, bits=8, stop=1, parity=None) # init with given parameters
+///
+/// Bits can be 8 or 9, stop can be 1 or 2, parity can be None, 0 (even), 1 (odd).
+///
+/// Extra method:
+///
+///     uart.any()               # returns True if any characters waiting
+
+struct _pyb_uart_obj_t {
+    mp_obj_base_t base;
+    pyb_uart_t uart_id;
+    bool is_enabled;
+//    UART_HandleTypeDef uart;
+};
+
+pyb_uart_obj_t *pyb_uart_global_debug = NULL;
+
+// assumes Init parameters have been set up correctly
+bool uart_init2(pyb_uart_obj_t *uart_obj) {
+#if 0
+    USART_TypeDef *UARTx = NULL;
+
+    uint32_t GPIO_Pin = 0;
+    uint8_t  GPIO_AF_UARTx = 0;
+    GPIO_TypeDef* GPIO_Port = NULL;
+
+    switch (uart_obj->uart_id) {
+        // USART1 is on PA9/PA10 (CK on PA8), PB6/PB7
+        case PYB_UART_1:
+            UARTx = USART1;
+            GPIO_AF_UARTx = GPIO_AF7_USART1;
+
+#if defined (PYBV4) || defined(PYBV10)
+            GPIO_Port = GPIOB;
+            GPIO_Pin = GPIO_PIN_6 | GPIO_PIN_7;
+#else
+            GPIO_Port = GPIOA;
+            GPIO_Pin = GPIO_PIN_9 | GPIO_PIN_10;
+#endif
+
+            __USART1_CLK_ENABLE();
+            break;
+
+        // USART2 is on PA2/PA3 (CK on PA4), PD5/PD6 (CK on PD7)
+        case PYB_UART_2:
+            UARTx = USART2;
+            GPIO_AF_UARTx = GPIO_AF7_USART2;
+
+            GPIO_Port = GPIOA;
+            GPIO_Pin = GPIO_PIN_2 | GPIO_PIN_3;
+
+            __USART2_CLK_ENABLE();
+            break;
+
+        // USART3 is on PB10/PB11 (CK on PB12), PC10/PC11 (CK on PC12), PD8/PD9 (CK on PD10)
+        case PYB_UART_3:
+            UARTx = USART3;
+            GPIO_AF_UARTx = GPIO_AF7_USART3;
+
+#if defined(PYBV3) || defined(PYBV4) | defined(PYBV10)
+            GPIO_Port = GPIOB;
+            GPIO_Pin = GPIO_PIN_10 | GPIO_PIN_11;
+#else
+            GPIO_Port = GPIOD;
+            GPIO_Pin = GPIO_PIN_8 | GPIO_PIN_9;
+#endif
+            __USART3_CLK_ENABLE();
+            break;
+
+        // UART4 is on PA0/PA1, PC10/PC11
+        case PYB_UART_4:
+            UARTx = UART4;
+            GPIO_AF_UARTx = GPIO_AF8_UART4;
+
+            GPIO_Port = GPIOA;
+            GPIO_Pin = GPIO_PIN_0 | GPIO_PIN_1;
+
+            __UART4_CLK_ENABLE();
+            break;
+
+        // USART6 is on PC6/PC7 (CK on PC8)
+        case PYB_UART_6:
+            UARTx = USART6;
+            GPIO_AF_UARTx = GPIO_AF8_USART6;
+
+            GPIO_Port = GPIOC;
+            GPIO_Pin = GPIO_PIN_6 | GPIO_PIN_7;
+
+            __USART6_CLK_ENABLE();
+            break;
+
+        default:
+            return false;
+    }
+
+    // init GPIO
+    GPIO_InitTypeDef GPIO_InitStructure;
+    GPIO_InitStructure.Pin = GPIO_Pin;
+    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
+    GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+    GPIO_InitStructure.Pull = GPIO_PULLUP;
+    GPIO_InitStructure.Alternate = GPIO_AF_UARTx;
+    HAL_GPIO_Init(GPIO_Port, &GPIO_InitStructure);
+
+    // init UARTx
+    uart_obj->uart.Instance = UARTx;
+    HAL_UART_Init(&uart_obj->uart);
+
+    uart_obj->is_enabled = true;
+#endif
+    return true;
+}
+
+bool uart_init(pyb_uart_obj_t *uart_obj, uint32_t baudrate) {
+#if 0
+    UART_HandleTypeDef *uh = &uart_obj->uart;
+    memset(uh, 0, sizeof(*uh));
+    uh->Init.BaudRate = baudrate;
+    uh->Init.WordLength = UART_WORDLENGTH_8B;
+    uh->Init.StopBits = UART_STOPBITS_1;
+    uh->Init.Parity = UART_PARITY_NONE;
+    uh->Init.Mode = UART_MODE_TX_RX;
+    uh->Init.HwFlowCtl = UART_HWCONTROL_NONE;
+    uh->Init.OverSampling = UART_OVERSAMPLING_16;
+#endif
+    return uart_init2(uart_obj);
+}
+
+void uart_deinit(pyb_uart_obj_t *uart_obj) {
+#if 0
+    uart_obj->is_enabled = false;
+    UART_HandleTypeDef *uart = &uart_obj->uart;
+    HAL_UART_DeInit(uart);
+    if (uart->Instance == USART1) {
+        __USART1_FORCE_RESET();
+        __USART1_RELEASE_RESET();
+        __USART1_CLK_DISABLE();
+    } else if (uart->Instance == USART2) {
+        __USART2_FORCE_RESET();
+        __USART2_RELEASE_RESET();
+        __USART2_CLK_DISABLE();
+    } else if (uart->Instance == USART3) {
+        __USART3_FORCE_RESET();
+        __USART3_RELEASE_RESET();
+        __USART3_CLK_DISABLE();
+    } else if (uart->Instance == UART4) {
+        __UART4_FORCE_RESET();
+        __UART4_RELEASE_RESET();
+        __UART4_CLK_DISABLE();
+    } else if (uart->Instance == USART6) {
+        __USART6_FORCE_RESET();
+        __USART6_RELEASE_RESET();
+        __USART6_CLK_DISABLE();
+    }
+#endif
+}
+
+bool uart_rx_any(pyb_uart_obj_t *uart_obj) {
+#if 0
+    return __HAL_UART_GET_FLAG(&uart_obj->uart, UART_FLAG_RXNE);
+#else
+    return false;
+#endif
+}
+
+int uart_rx_char(pyb_uart_obj_t *uart_obj) {
+    uint8_t ch;
+#if 0
+    if (HAL_UART_Receive(&uart_obj->uart, &ch, 1, 0) != HAL_OK) {
+        ch = 0;
+    }
+#else
+    ch = 'A';
+#endif
+    return ch;
+}
+
+void uart_tx_char(pyb_uart_obj_t *uart_obj, int c) {
+#if 0
+    uint8_t ch = c;
+    HAL_UART_Transmit(&uart_obj->uart, &ch, 1, 100000);
+#endif
+}
+
+void uart_tx_str(pyb_uart_obj_t *uart_obj, const char *str) {
+#if 0
+    HAL_UART_Transmit(&uart_obj->uart, (uint8_t*)str, strlen(str), 100000);
+#endif
+}
+
+void uart_tx_strn(pyb_uart_obj_t *uart_obj, const char *str, uint len) {
+#if 0
+    HAL_UART_Transmit(&uart_obj->uart, (uint8_t*)str, len, 100000);
+#endif
+}
+
+void uart_tx_strn_cooked(pyb_uart_obj_t *uart_obj, const char *str, uint len) {
+    for (const char *top = str + len; str < top; str++) {
+        if (*str == '\n') {
+            uart_tx_char(uart_obj, '\r');
+        }
+        uart_tx_char(uart_obj, *str);
+    }
+}
+
+/******************************************************************************/
+/* Micro Python bindings                                                      */
+
+STATIC void pyb_uart_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
+    pyb_uart_obj_t *self = self_in;
+    if (!self->is_enabled) {
+        print(env, "UART(%lu)", self->uart_id);
+    } else {
+#if 0
+        print(env, "UART(%lu, baudrate=%u, bits=%u, stop=%u",
+            self->uart_id, self->uart.Init.BaudRate,
+            self->uart.Init.WordLength == UART_WORDLENGTH_8B ? 8 : 9,
+            self->uart.Init.StopBits == UART_STOPBITS_1 ? 1 : 2);
+        if (self->uart.Init.Parity == UART_PARITY_NONE) {
+            print(env, ", parity=None)");
+        } else {
+            print(env, ", parity=%u)", self->uart.Init.Parity == UART_PARITY_EVEN ? 0 : 1);
+        }
+#endif
+    }
+}
+
+/// \method init(baudrate, *, bits=8, stop=1, parity=None)
+///
+/// Initialise the SPI bus with the given parameters:
+///
+///   - `baudrate` is the clock rate.
+///   - `bits` is the number of bits per byte, 8 or 9.
+///   - `stop` is the number of stop bits, 1 or 2.
+///   - `parity` is the parity, `None`, 0 (even) or 1 (odd).
+STATIC const mp_arg_t pyb_uart_init_args[] = {
+    { MP_QSTR_baudrate, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = 9600} },
+    { MP_QSTR_bits,     MP_ARG_KW_ONLY | MP_ARG_INT,  {.u_int = 8} },
+    { MP_QSTR_stop,     MP_ARG_KW_ONLY | MP_ARG_INT,  {.u_int = 1} },
+    { MP_QSTR_parity,   MP_ARG_KW_ONLY | MP_ARG_OBJ,  {.u_obj = mp_const_none} },
+};
+#define PYB_UART_INIT_NUM_ARGS ARRAY_SIZE(pyb_uart_init_args)
+
+STATIC mp_obj_t pyb_uart_init_helper(pyb_uart_obj_t *self, uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
+    // parse args
+    mp_arg_val_t vals[PYB_UART_INIT_NUM_ARGS];
+    mp_arg_parse_all(n_args, args, kw_args, PYB_UART_INIT_NUM_ARGS, pyb_uart_init_args, vals);
+#if 0
+    // set the UART configuration values
+    memset(&self->uart, 0, sizeof(self->uart));
+    UART_InitTypeDef *init = &self->uart.Init;
+    init->BaudRate = vals[0].u_int;
+    init->WordLength = vals[1].u_int == 8 ? UART_WORDLENGTH_8B : UART_WORDLENGTH_9B;
+    switch (vals[2].u_int) {
+        case 1: init->StopBits = UART_STOPBITS_1; break;
+        default: init->StopBits = UART_STOPBITS_2; break;
+    }
+    if (vals[3].u_obj == mp_const_none) {
+        init->Parity = UART_PARITY_NONE;
+    } else {
+        machine_int_t parity = mp_obj_get_int(vals[3].u_obj);
+        init->Parity = (parity & 1) ? UART_PARITY_ODD : UART_PARITY_EVEN;
+    }
+    init->Mode = UART_MODE_TX_RX;
+    init->HwFlowCtl = UART_HWCONTROL_NONE;
+    init->OverSampling = UART_OVERSAMPLING_16;
+
+    // init UART (if it fails, it's because the port doesn't exist)
+    if (!uart_init2(self)) {
+        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART port %d does not exist", self->uart_id));
+    }
+#endif
+
+    return mp_const_none;
+}
+
+/// \classmethod \constructor(bus, ...)
+///
+/// Construct a UART object on the given bus.  `bus` can be 1-6, or 'XA', 'XB', 'YA', or 'YB'.
+/// With no additional parameters, the UART object is created but not
+/// initialised (it has the settings from the last initialisation of
+/// the bus, if any).  If extra arguments are given, the bus is initialised.
+/// See `init` for parameters of initialisation.
+///
+/// The physical pins of the UART busses are:
+///
+///   - `UART(4)` is on `XA`: `(TX, RX) = (X1, X2) = (PA0, PA1)`
+///   - `UART(1)` is on `XB`: `(TX, RX) = (X9, X10) = (PB6, PB7)`
+///   - `UART(6)` is on `YA`: `(TX, RX) = (Y1, Y2) = (PC6, PC7)`
+///   - `UART(3)` is on `YB`: `(TX, RX) = (Y9, Y10) = (PB10, PB11)`
+///   - `UART(2)` is on: `(TX, RX) = (X3, X4) = (PA2, PA3)`
+STATIC mp_obj_t pyb_uart_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
+    // check arguments
+    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
+
+    // create object
+    pyb_uart_obj_t *o = m_new_obj(pyb_uart_obj_t);
+    o->base.type = &pyb_uart_type;
+
+    // work out port
+    o->uart_id = 0;
+#if 0
+    if (MP_OBJ_IS_STR(args[0])) {
+        const char *port = mp_obj_str_get_str(args[0]);
+        if (0) {
+#if defined(PYBV10)
+        } else if (strcmp(port, "XA") == 0) {
+            o->uart_id = PYB_UART_XA;
+        } else if (strcmp(port, "XB") == 0) {
+            o->uart_id = PYB_UART_XB;
+        } else if (strcmp(port, "YA") == 0) {
+            o->uart_id = PYB_UART_YA;
+        } else if (strcmp(port, "YB") == 0) {
+            o->uart_id = PYB_UART_YB;
+#endif
+        } else {
+            nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART port %s does not exist", port));
+        }
+    } else {
+        o->uart_id = mp_obj_get_int(args[0]);
+    }
+#endif
+
+    if (n_args > 1 || n_kw > 0) {
+        // start the peripheral
+        mp_map_t kw_args;
+        mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
+        pyb_uart_init_helper(o, n_args - 1, args + 1, &kw_args);
+    }
+
+    return o;
+}
+
+STATIC mp_obj_t pyb_uart_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
+    return pyb_uart_init_helper(args[0], n_args - 1, args + 1, kw_args);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_uart_init_obj, 1, pyb_uart_init);
+
+/// \method deinit()
+/// Turn off the UART bus.
+STATIC mp_obj_t pyb_uart_deinit(mp_obj_t self_in) {
+    pyb_uart_obj_t *self = self_in;
+    uart_deinit(self);
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_deinit_obj, pyb_uart_deinit);
+
+/// \method any()
+/// Return `True` if any characters waiting, else `False`.
+STATIC mp_obj_t pyb_uart_any(mp_obj_t self_in) {
+    pyb_uart_obj_t *self = self_in;
+    if (uart_rx_any(self)) {
+        return mp_const_true;
+    } else {
+        return mp_const_false;
+    }
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_uart_any_obj, pyb_uart_any);
+
+/// \method send(send, *, timeout=5000)
+/// Send data on the bus:
+///
+///   - `send` is the data to send (an integer to send, or a buffer object).
+///   - `timeout` is the timeout in milliseconds to wait for the send.
+///
+/// Return value: `None`.
+STATIC const mp_arg_t pyb_uart_send_args[] = {
+    { MP_QSTR_send,    MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+    { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} },
+};
+#define PYB_UART_SEND_NUM_ARGS ARRAY_SIZE(pyb_uart_send_args)
+
+STATIC mp_obj_t pyb_uart_send(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
+    // TODO assumes transmission size is 8-bits wide
+
+    pyb_uart_obj_t *self = args[0];
+
+    // parse args
+    mp_arg_val_t vals[PYB_UART_SEND_NUM_ARGS];
+    mp_arg_parse_all(n_args - 1, args + 1, kw_args, PYB_UART_SEND_NUM_ARGS, pyb_uart_send_args, vals);
+
+#if 0
+    // get the buffer to send from
+    mp_buffer_info_t bufinfo;
+    uint8_t data[1];
+    pyb_buf_get_for_send(vals[0].u_obj, &bufinfo, data);
+
+    // send the data
+    HAL_StatusTypeDef status = HAL_UART_Transmit(&self->uart, bufinfo.buf, bufinfo.len, vals[1].u_int);
+
+    if (status != HAL_OK) {
+        // TODO really need a HardwareError object, or something
+        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_Exception, "HAL_UART_Transmit failed with code %d", status));
+    }
+#else
+    (void)self;
+#endif
+
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_uart_send_obj, 1, pyb_uart_send);
+
+/// \method recv(recv, *, timeout=5000)
+///
+/// Receive data on the bus:
+///
+///   - `recv` can be an integer, which is the number of bytes to receive,
+///     or a mutable buffer, which will be filled with received bytes.
+///   - `timeout` is the timeout in milliseconds to wait for the receive.
+///
+/// Return value: if `recv` is an integer then a new buffer of the bytes received,
+/// otherwise the same buffer that was passed in to `recv`.
+STATIC const mp_arg_t pyb_uart_recv_args[] = {
+    { MP_QSTR_recv,    MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
+    { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 5000} },
+};
+#define PYB_UART_RECV_NUM_ARGS ARRAY_SIZE(pyb_uart_recv_args)
+
+STATIC mp_obj_t pyb_uart_recv(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
+    // TODO assumes transmission size is 8-bits wide
+
+    pyb_uart_obj_t *self = args[0];
+
+#if 0
+    // parse args
+    mp_arg_val_t vals[PYB_UART_RECV_NUM_ARGS];
+    mp_arg_parse_all(n_args - 1, args + 1, kw_args, PYB_UART_RECV_NUM_ARGS, pyb_uart_recv_args, vals);
+
+    // get the buffer to receive into
+    mp_buffer_info_t bufinfo;
+    mp_obj_t o_ret = pyb_buf_get_for_recv(vals[0].u_obj, &bufinfo);
+
+    // receive the data
+    HAL_StatusTypeDef status = HAL_UART_Receive(&self->uart, bufinfo.buf, bufinfo.len, vals[1].u_int);
+
+    if (status != HAL_OK) {
+        // TODO really need a HardwareError object, or something
+        nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_Exception, "HAL_UART_Receive failed with code %d", status));
+    }
+
+    // return the received data
+    if (o_ret == MP_OBJ_NULL) {
+        return vals[0].u_obj;
+    } else {
+        return mp_obj_str_builder_end(o_ret);
+    }
+#else
+    (void)self;
+    return mp_const_none;
+#endif
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_uart_recv_obj, 1, pyb_uart_recv);
+
+STATIC const mp_map_elem_t pyb_uart_locals_dict_table[] = {
+    // instance methods
+    { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_uart_init_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&pyb_uart_deinit_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_any), (mp_obj_t)&pyb_uart_any_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&pyb_uart_send_obj },
+    { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&pyb_uart_recv_obj },
+};
+
+STATIC MP_DEFINE_CONST_DICT(pyb_uart_locals_dict, pyb_uart_locals_dict_table);
+
+const mp_obj_type_t pyb_uart_type = {
+    { &mp_type_type },
+    .name = MP_QSTR_UART,
+    .print = pyb_uart_print,
+    .make_new = pyb_uart_make_new,
+    .locals_dict = (mp_obj_t)&pyb_uart_locals_dict,
+};
diff --git a/teensy/usart.c b/teensy/usart.c
deleted file mode 100644
index a700e8e379..0000000000
--- a/teensy/usart.c
+++ /dev/null
@@ -1,38 +0,0 @@
-#include "misc.h"
-#include "mpconfig.h"
-#include "qstr.h"
-#include "obj.h"
-#include "../stm/usart.h"
-
-pyb_usart_t pyb_usart_global_debug = PYB_USART_NONE;
-
-void usart_init(pyb_usart_t usart_id, uint32_t baudrate)
-{
-    (void)usart_id;
-    (void)baudrate;
-}
-
-bool usart_rx_any(pyb_usart_t usart_id)
-{
-    (void)usart_id;
-    return false;
-}
-
-int usart_rx_char(pyb_usart_t usart_id)
-{
-    (void)usart_id;
-    return 0;
-}
-
-void usart_tx_str(pyb_usart_t usart_id, const char *str)
-{
-    (void)usart_id;
-    (void)str;
-}
-
-void usart_tx_strn_cooked(pyb_usart_t usart_id, const char *str, int len)
-{
-  (void)usart_id;
-  (void)str;
-  (void)len;
-}
diff --git a/teensy/usb.c b/teensy/usb.c
index a045a2ed69..ab4731f27b 100644
--- a/teensy/usb.c
+++ b/teensy/usb.c
@@ -13,8 +13,13 @@ int usb_vcp_is_enabled(void)
   return 1;
 }
 
-int usb_vcp_rx_any(void)
-{
+void usb_vcp_set_interrupt_char(int c) {
+  // The teensy 3.1 usb stack doesn't currently have the notion of generating
+  // an exception when a certain character is received. That just means that
+  // you can't press Control-C and get your python script to stop.
+}
+
+int usb_vcp_rx_num(void) {
   return usb_serial_available();
 }