diff --git a/.gitignore b/.gitignore
index 95be7bf651f87a8d5eb2e316be7e1cb6146a6b40..6224e57cdbd541cb20b20160584f3a08fa4e4a02 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,6 @@
 ######################
 *.swp
 
+# Build directory
+######################
+build/
diff --git a/stm/printf.c b/stm/printf.c
index 8a40a61a5fa2f84e2ae775f051cd58ab52d0a771..fa94269257c0ea451bbc0c19106633da25bf32c6 100644
--- a/stm/printf.c
+++ b/stm/printf.c
@@ -221,10 +221,12 @@ void stdout_print_strn(void *data, const char *str, unsigned int len) {
     bool any = false;
 
     // TODO should have a setting for which USART port to send to
+#if 0 // if 0'd out so that we're not calling functions with the wrong arguments
     if (usart_is_enabled()) {
         usart_tx_strn_cooked(str, len);
         any = true;
     }
+#endif
 
     if (usb_vcp_is_enabled()) {
         usb_vcp_send_strn_cooked(str, len);
diff --git a/teensy/Makefile b/teensy/Makefile
index ade9b37040b10d4492c60cd036f5f0cf85b68dd3..8046f862ac6df096bd370aed05d86fa85130e20f 100644
--- a/teensy/Makefile
+++ b/teensy/Makefile
@@ -33,7 +33,8 @@ SRC_C = \
 	lcd.c \
 	led.c \
 	lexerfatfs.c \
-	usart.c \
+	lexermemzip.c \
+	memzip.c \
 	usb.c \
 
 STM_SRC_C = \
@@ -60,9 +61,9 @@ OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(STM_SRC_C:.c=.o) $(STM_SRC_S:.s=.o
 #LIB += -ltermcap
 
 all2: $(BUILD) hex
-hex: $(BUILD)/flash.hex
+hex: $(BUILD)/micropython-mz.hex
 
-post_compile: $(BUILD)/flash.hex
+post_compile: $(BUILD)/micropython-mz.hex
 	$(ECHO) "Preparing $@ for upload"
 	$(Q)$(TOOLS_PATH)/teensy_post_compile -file="$(basename $(<F))" -path="$(<D)" -tools="$(TOOLS_PATH)"
 
@@ -72,11 +73,19 @@ reboot:
 
 upload: post_compile reboot
 
-$(BUILD)/flash.elf: $(OBJ)
+$(BUILD)/micropython.elf: $(OBJ)
 	$(ECHO) "LINK $<"
 	$(Q)$(CC) $(LDFLAGS) -o "$@" -Wl,-Map,$(@:.elf=.map) $(OBJ) $(LIBS)
 	$(Q)$(SIZE) $@
 
+ifeq ($(MEMZIP_DIR),)
+MEMZIP_DIR = memzip_files
+endif
+
+$(BUILD)/micropython-mz.hex: $(BUILD)/micropython.hex $(shell find ${MEMZIP_DIR} -type f)
+	@$(ECHO) "Creating $@"
+	$(Q)./add-memzip.sh $< $@ ${MEMZIP_DIR}
+
 $(BUILD)/%.hex: $(BUILD)/%.elf
 	$(ECHO) "HEX $<"
 	$(Q)$(OBJCOPY) -O ihex -R .eeprom "$<" "$@"
diff --git a/teensy/add-memzip.sh b/teensy/add-memzip.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a00489effd77cc1c6d5fb5e23a365c81eac7cc39
--- /dev/null
+++ b/teensy/add-memzip.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+if [ "$#" != 3 ]; then
+    echo "Usage: add-memzip.sh input.hex output.hex file-directory"
+    exit 1
+fi
+
+#set -x
+
+input_hex=$1
+output_hex=$2
+memzip_src_dir=$3
+
+input_bin=${input_hex}.bin
+output_bin=${output_hex}.bin
+zip_file=${output_hex}.zip
+zip_base=$(basename ${zip_file})
+zip_dir=$(dirname ${zip_file})
+abs_zip_dir=$(realpath ${zip_dir})
+
+rm -f ${zip_file}
+(cd ${memzip_src_dir}; zip -0 -r -D ${abs_zip_dir}/${zip_base} .)
+objcopy -I ihex -O binary ${input_hex} ${input_bin}
+cat ${input_bin} ${zip_file} > ${output_bin}
+objcopy -I binary -O ihex ${output_bin} ${output_hex}
+echo "Added ${memzip_src_dir} to ${input_hex} creating ${output_hex}"
+
diff --git a/teensy/lexermemzip.c b/teensy/lexermemzip.c
new file mode 100644
index 0000000000000000000000000000000000000000..2f808ee42913a3274a2acca36095026f8747447a
--- /dev/null
+++ b/teensy/lexermemzip.c
@@ -0,0 +1,18 @@
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "misc.h"
+#include "lexer.h"
+#include "memzip.h"
+
+mp_lexer_t *mp_lexer_new_from_memzip_file(const char *filename)
+{
+    void *data;
+    size_t len;
+
+    if (memzip_locate(filename, &data, &len) != MZ_OK) {
+        return NULL;
+    }
+    return mp_lexer_new_from_str_len(filename, (const char *)data, (uint)len, 0);
+}
+
diff --git a/teensy/lexermemzip.h b/teensy/lexermemzip.h
new file mode 100644
index 0000000000000000000000000000000000000000..e5d4be5eaecf99fccfefbcacc42185e5f61c69f1
--- /dev/null
+++ b/teensy/lexermemzip.h
@@ -0,0 +1,2 @@
+mp_lexer_t *mp_lexer_new_from_memzip_file(const char *filename);
+
diff --git a/teensy/main.c b/teensy/main.c
index fd5aec0459fcfcde288bc95f57a8be3682ba5441..d5860cd9d48040c778f476ea9dad1047d7d6980e 100644
--- a/teensy/main.c
+++ b/teensy/main.c
@@ -8,6 +8,7 @@
 #include "mpconfig.h"
 #include "mpqstr.h"
 #include "lexer.h"
+#include "lexermemzip.h"
 #include "parse.h"
 #include "obj.h"
 #include "compile.h"
@@ -22,52 +23,17 @@
 
 extern uint32_t _heap_start;
 
-#ifdef USE_READLINE
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif
+bool do_file(const char *filename);
 
-#if 0
-static char *str_join(const char *s1, int sep_char, const char *s2) {
-    int l1 = strlen(s1);
-    int l2 = strlen(s2);
-    char *s = m_new(char, l1 + l2 + 2);
-    memcpy(s, s1, l1);
-    if (sep_char != 0) {
-        s[l1] = sep_char;
-        l1 += 1;
+void flash_error(int n) {
+    for (int i = 0; i < n; i++) {
+        led_state(PYB_LED_BUILTIN, 1);
+        delay(250);
+        led_state(PYB_LED_BUILTIN, 0);
+        delay(250);
     }
-    memcpy(s + l1, s2, l2);
-    s[l1 + l2] = 0;
-    return s;
 }
 
-static char *prompt(char *p) {
-#ifdef USE_READLINE
-    char *line = readline(p);
-    if (line) {
-        add_history(line);
-    }
-#else
-    static char buf[256];
-    fputs(p, stdout);
-    char *s = fgets(buf, sizeof(buf), stdin);
-    if (!s) {
-        return NULL;
-    }
-    int l = strlen(buf);
-    if (buf[l - 1] == '\n') {
-        buf[l - 1] = 0;
-    } else {
-        l++;
-    }
-    char *line = m_new(char, l);
-    memcpy(line, buf, l);
-#endif
-    return line;
-}
-#endif
-
 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"
@@ -215,6 +181,19 @@ mp_obj_t pyb_hid_send_report(mp_obj_t arg) {
 }
 #endif
 
+static qstr pyb_config_source_dir = 0;
+static qstr pyb_config_main = 0;
+
+mp_obj_t pyb_source_dir(mp_obj_t source_dir) {
+    pyb_config_source_dir = mp_obj_get_qstr(source_dir);
+    return mp_const_none;
+}
+
+mp_obj_t pyb_main(mp_obj_t main) {
+    pyb_config_main = mp_obj_get_qstr(main);
+    return mp_const_none;
+}
+
 mp_obj_t pyb_delay(mp_obj_t count) {
     delay(mp_obj_get_int(count));
     return mp_const_none;
@@ -225,6 +204,12 @@ 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_get_qstr(filename_obj));
+    do_file(filename);
+    return mp_const_none;
+}
+
 char *strdup(const char *str) {
     uint32_t len = strlen(str);
     char *s2 = m_new(char, len + 1);
@@ -316,6 +301,39 @@ int readline(vstr_t *line, const char *prompt) {
     }
 }
 
+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_node_t pn = mp_parse(lex, MP_PARSE_FILE_INPUT);
+    mp_lexer_free(lex);
+
+    if (pn == MP_PARSE_NODE_NULL) {
+        return false;
+    }
+
+    mp_obj_t module_fun = mp_compile(pn, false);
+    if (module_fun == mp_const_none) {
+        return false;
+    }
+
+    nlr_buf_t nlr;
+    if (nlr_push(&nlr) == 0) {
+        rt_call_function_0(module_fun);
+        nlr_pop();
+        return true;
+    } else {
+        // uncaught exception
+        mp_obj_print((mp_obj_t)nlr.ret_val);
+        printf("\n");
+        return false;
+    }
+}
+
 void do_repl(void) {
     stdout_tx_str("Micro Python for Teensy 3.1\r\n");
     stdout_tx_str("Type \"help()\" for more information.\r\n");
@@ -397,24 +415,53 @@ soft_reset:
     rt_init();
 
 #if 1
-    printf("About to add functions()\n");
     // add some functions to the python namespace
     {
         rt_store_name(qstr_from_str_static("help"), rt_make_function_0(pyb_help));
         mp_obj_t m = mp_obj_new_module(qstr_from_str_static("pyb"));
         rt_store_attr(m, qstr_from_str_static("info"), rt_make_function_0(pyb_info));
+        rt_store_attr(m, qstr_from_str_static("source_dir"), rt_make_function_1(pyb_source_dir));
+        rt_store_attr(m, qstr_from_str_static("main"), rt_make_function_1(pyb_main));
         rt_store_attr(m, qstr_from_str_static("gc"), rt_make_function_0(pyb_gc));
         rt_store_attr(m, qstr_from_str_static("delay"), rt_make_function_1(pyb_delay));
         rt_store_attr(m, qstr_from_str_static("led"), rt_make_function_1(pyb_led));
         rt_store_attr(m, qstr_from_str_static("Led"), rt_make_function_1(pyb_Led));
         rt_store_attr(m, qstr_from_str_static("gpio"), (mp_obj_t)&pyb_gpio_obj);
         rt_store_name(qstr_from_str_static("pyb"), m);
+        rt_store_name(qstr_from_str_static("run"), rt_make_function_1(pyb_run));
     }
 #endif
 
+    if (!do_file("/boot.py")) {
+        printf("Unable to open '/boot.py'\n");
+        flash_error(4);
+    }
+
     // Turn bootup LED off
     led_state(PYB_LED_BUILTIN, 0);
 
+    // run main script
+    {
+        vstr_t *vstr = vstr_new();
+        vstr_add_str(vstr, "/");
+        if (pyb_config_source_dir == 0) {
+            vstr_add_str(vstr, "src");
+        } else {
+            vstr_add_str(vstr, qstr_str(pyb_config_source_dir));
+        }
+        vstr_add_char(vstr, '/');
+        if (pyb_config_main == 0) {
+            vstr_add_str(vstr, "main.py");
+        } else {
+            vstr_add_str(vstr, qstr_str(pyb_config_main));
+        }
+        if (!do_file(vstr_str(vstr))) {
+            printf("Unable to open '%s'\n", vstr_str(vstr));
+            flash_error(3);
+        }
+        vstr_free(vstr);
+    }
+
     do_repl();
 
     printf("PYB: soft reboot\n");
diff --git a/teensy/memzip.c b/teensy/memzip.c
new file mode 100644
index 0000000000000000000000000000000000000000..ec6c26980cfe8845df6919dca27ea37cff2f7b7f
--- /dev/null
+++ b/teensy/memzip.c
@@ -0,0 +1,37 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "memzip.h"
+
+extern uint8_t _staticfs[];
+
+MEMZIP_RESULT memzip_locate(const char *filename, void **data, size_t *len)
+{
+    const MEMZIP_FILE_HDR *file_hdr = (const MEMZIP_FILE_HDR *)_staticfs;
+    uint8_t *mem_data;
+
+    /* Zip file filenames don't have a leading /, so we strip it off */
+
+    if (*filename == '/') {
+        filename++;
+    }
+    while (file_hdr->signature == MEMZIP_FILE_HEADER_SIGNATURE) {
+        const char *file_hdr_filename = (const char *)&file_hdr[1];
+        mem_data = (uint8_t *)file_hdr_filename;
+        mem_data += file_hdr->filename_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;
+        }
+        mem_data += file_hdr->uncompressed_size;
+        file_hdr = (const MEMZIP_FILE_HDR *)mem_data;
+    }
+    return MZ_NO_FILE;
+}
diff --git a/teensy/memzip.h b/teensy/memzip.h
new file mode 100644
index 0000000000000000000000000000000000000000..ecbd98763ce01efc8de973c8962e3abebf68149b
--- /dev/null
+++ b/teensy/memzip.h
@@ -0,0 +1,73 @@
+#pragma pack(push, 1)
+
+#define MEMZIP_FILE_HEADER_SIGNATURE 0x04034b50
+typedef struct
+{
+    uint32_t    signature;
+    uint16_t    version;
+    uint16_t    flags;
+    uint16_t    compression_method;
+    uint16_t    last_mod_time;
+    uint16_t    last_mod_date;
+    uint32_t    crc32;
+    uint32_t    compressed_size;
+    uint32_t    uncompressed_size;
+    uint16_t    filename_len;
+    uint16_t    extra_len;
+
+    /* char     filename[filename_len]  */
+    /* uint8_t  extra[extra_len]        */
+
+} MEMZIP_FILE_HDR;
+
+#define MEMZIP_CENTRAL_DIRECTORY_SIGNATURE 0x02014b50
+typedef struct
+{
+    uint32_t    signature;
+    uint16_t    version_made_by;
+    uint16_t    version_read_with;
+    uint16_t    flags;
+    uint16_t    compression_method;
+    uint16_t    last_mod_time;
+    uint16_t    last_mod_date;
+    uint32_t    crc32;
+    uint32_t    compressed_size;
+    uint32_t    uncompressed_size;
+    uint16_t    filename_len;
+    uint16_t    extra_len;
+    uint16_t    disk_num;
+    uint16_t    internal_file_attributes;
+    uint32_t    external_file_attributes;
+    uint32_t    file_header_offset;
+
+    /* char     filename[filename_len]  */
+    /* uint8_t  extra[extra_len]        */
+
+} MEMZIP_CENTRAL_DIRECTORY_HDR;
+
+#define MEMZIP_END_OF_CENTRAL_DIRECTORY_SIGNATURE 0x06054b50
+typedef struct
+{
+    uint32_t    signature;
+    uint16_t    disk_num;
+    uint16_t    central_directory_disk;
+    uint16_t    num_central_directories_this_disk;
+    uint16_t    total_central_directories;
+    uint32_t    central_directory_size;
+    uint32_t    central_directory_offset;
+    uint16_t    comment_len;
+
+    /* char     comment[comment_len]    */
+
+} MEMZIP_END_OF_CENTRAL_DIRECTORY;
+
+#pragma pack(pop)
+
+typedef enum {
+    MZ_OK = 0,          /* (0) Succeeded */
+    MZ_NO_FILE,         /* (1) Could not find the file. */
+    MZ_FILE_COMPRESSED, /* (2) File is compressed (expecting uncompressed) */
+
+} MEMZIP_RESULT;
+
+MEMZIP_RESULT memzip_locate(const char *filename, void **data, size_t *len);
diff --git a/teensy/memzip_files/boot.py b/teensy/memzip_files/boot.py
new file mode 100644
index 0000000000000000000000000000000000000000..3fe9f05e53cdb42df7294bca0afb160976542396
--- /dev/null
+++ b/teensy/memzip_files/boot.py
@@ -0,0 +1 @@
+print("Executing boot.py")
diff --git a/teensy/memzip_files/src/main.py b/teensy/memzip_files/src/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..8fc08db15ebcc690d10e8354ff8d62f1cc357592
--- /dev/null
+++ b/teensy/memzip_files/src/main.py
@@ -0,0 +1,11 @@
+print("Executing main.py")
+
+x=pyb.led(1)
+pyb.delay(100)
+x=pyb.led(0)
+pyb.delay(100)
+x=pyb.led(1)
+pyb.delay(100)
+x=pyb.led(0)
+
+
diff --git a/teensy/memzip_files/src/test.py b/teensy/memzip_files/src/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..7957b27caf9fc7db4169eff2ea12e06b0fe27b02
--- /dev/null
+++ b/teensy/memzip_files/src/test.py
@@ -0,0 +1 @@
+print("Executing /src/test.py")
diff --git a/teensy/memzip_files/test.py b/teensy/memzip_files/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..088cb4383aef61ef31dc1184fa263201764e395d
--- /dev/null
+++ b/teensy/memzip_files/test.py
@@ -0,0 +1 @@
+print("Executing /test.py")
diff --git a/teensy/mk20dx256.ld b/teensy/mk20dx256.ld
index da57cbe5cb36578811977849e78dc5899ff98a6d..e46efd3e26be622c01a9bc8e079eab5c97a77117 100644
--- a/teensy/mk20dx256.ld
+++ b/teensy/mk20dx256.ld
@@ -126,6 +126,12 @@ SECTIONS
 		_edata = .; 
 	} > 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);
+
 	.noinit (NOLOAD) : {
 		*(.noinit*)
 	} > RAM