summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--teensy/Makefile155
-rw-r--r--teensy/README.md31
-rw-r--r--teensy/gchelper.s36
-rw-r--r--teensy/led.c86
-rw-r--r--teensy/led.h9
-rw-r--r--teensy/lexerteensy.c33
-rw-r--r--teensy/lexerteensy.h8
-rw-r--r--teensy/main.c474
-rw-r--r--teensy/main.cpp245
-rw-r--r--teensy/malloc0.c57
-rw-r--r--teensy/mk20dx256.ld164
-rw-r--r--teensy/mpconfigport.h21
-rw-r--r--teensy/printf.c305
-rw-r--r--teensy/std.h22
-rw-r--r--teensy/string0.c110
-rw-r--r--teensy/usb.c44
-rw-r--r--teensy/usb.h9
17 files changed, 1809 insertions, 0 deletions
diff --git a/teensy/Makefile b/teensy/Makefile
new file mode 100644
index 0000000000..98b467a06e
--- /dev/null
+++ b/teensy/Makefile
@@ -0,0 +1,155 @@
+ifeq ($(ARDUINO),)
+$(error Please define ARDUINO (where TeensyDuino is installed))
+endif
+TOOLS_PATH = $(ARDUINO)/hardware/tools
+COMPILER_PATH = $(TOOLS_PATH)/arm-none-eabi/bin
+CORE_PATH = $(ARDUINO)/hardware/teensy/cores/teensy3
+
+PYSRC=../py
+BUILD=build
+
+AS = $(COMPILER_PATH)/arm-none-eabi-as
+CC = $(COMPILER_PATH)/arm-none-eabi-gcc
+LD = $(COMPILER_PATH)/arm-none-eabi-ld
+OBJCOPY = $(COMPILER_PATH)/arm-none-eabi-objcopy
+SIZE = $(COMPILER_PATH)/arm-none-eabi-size
+
+CFLAGS_TEENSY = -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -D__MK20DX256__
+CFLAGS_CORTEX_M4 = -mthumb -mtune=cortex-m4 -mcpu=cortex-m4 -fsingle-precision-constant -Wdouble-promotion $(CFLAGS_TEENSY)
+CFLAGS = -I. -I$(PYSRC) -I$(CORE_PATH) -Wall -ansi -std=gnu99 -Os -DNDEBUG $(CFLAGS_CORTEX_M4)
+LDFLAGS = -nostdlib -T mk20dx256.ld
+LIBS = -L $(COMPILER_PATH)/../lib/gcc/arm-none-eabi/4.7.2/thumb2 -lgcc
+
+SRC_C = \
+ main.c \
+ lexerteensy.c \
+ led.c \
+ malloc0.c \
+ printf.c \
+ string0.c \
+ usb.c \
+
+SRC_S = \
+ gchelper.s \
+
+PY_O = \
+ nlrthumb.o \
+ gc.o \
+ malloc.o \
+ qstr.o \
+ vstr.o \
+ unicode.o \
+ lexer.o \
+ parse.o \
+ scope.o \
+ compile.o \
+ emitcommon.o \
+ emitpass1.o \
+ emitbc.o \
+ asmthumb.o \
+ emitnthumb.o \
+ emitinlinethumb.o \
+ runtime.o \
+ map.o \
+ obj.o \
+ objbool.o \
+ objboundmeth.o \
+ objcell.o \
+ objclass.o \
+ objclosure.o \
+ objcomplex.o \
+ objdict.o \
+ objexcept.o \
+ objfloat.o \
+ objfun.o \
+ objgenerator.o \
+ objinstance.o \
+ objint.o \
+ objlist.o \
+ objmodule.o \
+ objnone.o \
+ objrange.o \
+ objset.o \
+ objslice.o \
+ objstr.o \
+ objtuple.o \
+ objtype.o \
+ builtin.o \
+ builtinimport.o \
+ vm.o \
+ showbc.o \
+ repl.o \
+
+SRC_TEENSY = \
+ mk20dx128.c \
+ pins_teensy.c \
+ analog.c \
+ usb_desc.c \
+ usb_dev.c \
+ usb_mem.c \
+ usb_serial.c \
+ yield.c \
+
+OBJ = $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(SRC_S:.s=.o) $(PY_O) $(SRC_TEENSY:.c=.o))
+#LIB = -lreadline
+# the following is needed for BSD
+#LIB += -ltermcap
+PROG = micropython
+
+all: hex
+hex: $(PROG).hex
+
+post_compile: $(PROG).hex
+ $(TOOLS_PATH)/teensy_post_compile -file="$(basename $<)" -path="$(CURDIR)" -tools="$(TOOLS_PATH)"
+
+reboot:
+ -$(TOOLS_PATH)/teensy_reboot
+
+upload: post_compile reboot
+
+$(PROG).elf: $(BUILD) $(OBJ)
+ $(CC) $(LDFLAGS) -o "$@" -Wl,-Map,$(PROG).map $(OBJ) $(LIBS)
+
+%.hex: %.elf
+ $(SIZE) "$<"
+ $(OBJCOPY) -O ihex -R .eeprom "$<" "$@"
+
+$(BUILD):
+ mkdir -p $@
+
+$(BUILD)/%.o: %.s
+ $(AS) -o $@ $<
+
+$(BUILD)/%.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+$(BUILD)/%.o: $(PYSRC)/%.S
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+$(BUILD)/%.o: $(PYSRC)/%.c mpconfigport.h
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+$(BUILD)/%.o: $(CORE_PATH)/%.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+$(BUILD)/emitnthumb.o: $(PYSRC)/emitnative.c $(PYSRC)/emit.h
+ $(CC) $(CFLAGS) -DN_THUMB -c -o $@ $<
+
+# optimising gc for speed; 5ms down to 4ms
+$(BUILD)/gc.o: $(PYSRC)/gc.c
+ $(CC) $(CFLAGS) -O3 -c -o $@ $<
+
+# optimising vm for speed, adds only a small amount to code size but makes a huge difference to speed (20% faster)
+$(BUILD)/vm.o: $(PYSRC)/vm.c
+ $(CC) $(CFLAGS) -O3 -c -o $@ $<
+
+$(BUILD)/main.o: mpconfigport.h
+$(BUILD)/parse.o: $(PYSRC)/grammar.h
+$(BUILD)/compile.o: $(PYSRC)/grammar.h
+$(BUILD)/emitbc.o: $(PYSRC)/emit.h
+
+clean:
+ /bin/rm -rf $(BUILD)
+ /bin/rm -f $(PROG).elf $(PROG).hex $(PROG).map
+
+.PHONY: clean
diff --git a/teensy/README.md b/teensy/README.md
new file mode 100644
index 0000000000..31339cd796
--- /dev/null
+++ b/teensy/README.md
@@ -0,0 +1,31 @@
+Build Instructions for Teensy 3.1
+
+This assumes that you have TeensyDuino installed and set the ARUINO environment
+variable pointing to the where Arduino with TeensyDuino is installed.
+
+```
+cd teensy
+ARDUINO=~/arduino-1.0.5 make
+```
+
+To build the loader
+
+```
+cd teensy/loader
+make
+```
+
+To upload micropython to the Teensy 3.1.
+
+Press the Program button on the Teensy 3.1
+```
+make upload
+```
+
+Currently, the python prompt is through the USB serial interface.
+
+The LED will blink (100 msec on/100 msec off) while waiting for the USB Serial
+device to be configured, and will blink (200 msec on/200 msec off) while
+sitting at the readline prompt.
+
+Currently, there is no I/O support configured (no GPIO, ADC, etc).
diff --git a/teensy/gchelper.s b/teensy/gchelper.s
new file mode 100644
index 0000000000..f8735d2830
--- /dev/null
+++ b/teensy/gchelper.s
@@ -0,0 +1,36 @@
+ .syntax unified
+ .cpu cortex-m4
+ .thumb
+ .text
+ .align 2
+
+@ void gc_helper_get_regs_and_clean_stack(r0=uint regs[10], r1=heap_end)
+ .global gc_helper_get_regs_and_clean_stack
+ .thumb
+ .thumb_func
+ .type gc_helper_get_regs_and_clean_stack, %function
+gc_helper_get_regs_and_clean_stack:
+ @ store registers into given array
+ str r4, [r0], #4
+ str r5, [r0], #4
+ str r6, [r0], #4
+ str r7, [r0], #4
+ str r8, [r0], #4
+ str r9, [r0], #4
+ str r10, [r0], #4
+ str r11, [r0], #4
+ str r12, [r0], #4
+ str r13, [r0], #4
+
+ @ clean the stack from given pointer up to current sp
+ movs r0, #0
+ mov r2, sp
+ b.n .entry
+.loop:
+ str r0, [r1], #4
+.entry:
+ cmp r1, r2
+ bcc.n .loop
+ bx lr
+
+ .size gc_helper_get_regs_and_clean_stack, .-gc_helper_get_regs_and_clean_stack
diff --git a/teensy/led.c b/teensy/led.c
new file mode 100644
index 0000000000..1aad2a365e
--- /dev/null
+++ b/teensy/led.c
@@ -0,0 +1,86 @@
+#include <stdio.h>
+
+#include "misc.h"
+#include "mpconfig.h"
+#include "obj.h"
+#include "led.h"
+
+#include "Arduino.h"
+
+void led_init(void) {
+}
+
+void led_state(pyb_led_t led, int state) {
+ uint8_t pin;
+
+ if (led == 0) {
+ pin = LED_BUILTIN;
+ } else {
+ return;
+ }
+ digitalWrite(pin, state);
+}
+
+void led_toggle(pyb_led_t led) {
+ uint8_t pin;
+
+ if (led == 0) {
+ pin = LED_BUILTIN;
+ } else {
+ return;
+ }
+
+ digitalWrite(pin, !digitalRead(pin));
+}
+
+/******************************************************************************/
+/* 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) {
+ pyb_led_obj_t *self = self_in;
+ print(env, "<LED %lu>", self->led_id);
+}
+
+mp_obj_t led_obj_on(mp_obj_t self_in) {
+ pyb_led_obj_t *self = self_in;
+ led_state(self->led_id, 1);
+ return mp_const_none;
+}
+
+mp_obj_t led_obj_off(mp_obj_t self_in) {
+ pyb_led_obj_t *self = self_in;
+ led_state(self->led_id, 0);
+ 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_obj_type_t led_obj_type = {
+ { &mp_const_type },
+ "Led",
+ led_obj_print, // print
+ NULL, // make_new
+ NULL, // call_n
+ NULL, // unary_op
+ NULL, // binary_op
+ NULL, // getiter
+ NULL, // iternext
+ { // method list
+ { "on", &led_obj_on_obj },
+ { "off", &led_obj_off_obj },
+ { NULL, NULL },
+ }
+};
+
+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;
+}
diff --git a/teensy/led.h b/teensy/led.h
new file mode 100644
index 0000000000..c5a4812549
--- /dev/null
+++ b/teensy/led.h
@@ -0,0 +1,9 @@
+typedef enum {
+ PYB_LED_BUILTIN = 0,
+} 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);
diff --git a/teensy/lexerteensy.c b/teensy/lexerteensy.c
new file mode 100644
index 0000000000..eefaf66657
--- /dev/null
+++ b/teensy/lexerteensy.c
@@ -0,0 +1,33 @@
+#include <stdint.h>
+#include <stdio.h>
+
+#include "misc.h"
+#include "lexer.h"
+#include "lexerteensy.h"
+
+unichar str_buf_next_char(mp_lexer_str_buf_t *sb) {
+ if (sb->src_cur < sb->src_end) {
+ return *sb->src_cur++;
+ } else {
+ return MP_LEXER_CHAR_EOF;
+ }
+}
+
+void str_buf_free(mp_lexer_str_buf_t *sb) {
+ if (sb->free) {
+ m_del(char, (char*)sb->src_beg, 0 /* don't know allocated size of src */);
+ }
+}
+
+mp_lexer_t *mp_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str, mp_lexer_str_buf_t *sb) {
+ sb->free = free_str;
+ sb->src_beg = str;
+ sb->src_cur = str;
+ sb->src_end = str + len;
+ return mp_lexer_new(src_name, sb, (mp_lexer_stream_next_char_t)str_buf_next_char, (mp_lexer_stream_close_t)str_buf_free);
+}
+
+mp_lexer_t *mp_import_open_file(qstr mod_name) {
+ printf("import not implemented\n");
+ return NULL;
+}
diff --git a/teensy/lexerteensy.h b/teensy/lexerteensy.h
new file mode 100644
index 0000000000..961a70ada4
--- /dev/null
+++ b/teensy/lexerteensy.h
@@ -0,0 +1,8 @@
+typedef struct _py_lexer_str_buf_t {
+ bool free; // free src_beg when done
+ const char *src_beg; // beginning of source
+ const char *src_cur; // current location in source
+ const char *src_end; // end (exclusive) of source
+} mp_lexer_str_buf_t;
+
+mp_lexer_t *mp_lexer_new_from_str_len(const char *src_name, const char *str, uint len, bool free_str, mp_lexer_str_buf_t *sb);
diff --git a/teensy/main.c b/teensy/main.c
new file mode 100644
index 0000000000..3711cd60d6
--- /dev/null
+++ b/teensy/main.c
@@ -0,0 +1,474 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "mpqstr.h"
+#include "lexer.h"
+#include "lexerteensy.h"
+#include "parse.h"
+#include "obj.h"
+#include "compile.h"
+#include "runtime0.h"
+#include "runtime.h"
+#include "repl.h"
+#include "usb.h"
+#include "gc.h"
+#include "led.h"
+
+#include "Arduino.h"
+
+extern uint32_t _heap_start;
+
+#ifdef USE_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#if 0
+static char *str_join(const char *s1, int sep_char, const char *s2) {
+ int l1 = strlen(s1);
+ int l2 = strlen(s2);
+ char *s = m_new(char, l1 + l2 + 2);
+ memcpy(s, s1, l1);
+ if (sep_char != 0) {
+ s[l1] = sep_char;
+ l1 += 1;
+ }
+ memcpy(s + l1, s2, l2);
+ s[l1 + l2] = 0;
+ return s;
+}
+
+static char *prompt(char *p) {
+#ifdef USE_READLINE
+ char *line = readline(p);
+ if (line) {
+ add_history(line);
+ }
+#else
+ static char buf[256];
+ fputs(p, stdout);
+ char *s = fgets(buf, sizeof(buf), stdin);
+ if (!s) {
+ return NULL;
+ }
+ int l = strlen(buf);
+ if (buf[l - 1] == '\n') {
+ buf[l - 1] = 0;
+ } else {
+ l++;
+ }
+ char *line = m_new(char, l);
+ memcpy(line, buf, l);
+#endif
+ return line;
+}
+#endif
+
+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
+;
+
+// get some help about available functions
+static mp_obj_t pyb_help(void) {
+ printf("%s", help_text);
+ return mp_const_none;
+}
+
+// get lots of info about the board
+static mp_obj_t pyb_info(void) {
+ // 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
+ {
+ 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);
+ printf("_ebss=%p\n", &_ebss);
+ printf("_estack=%p\n", &_estack);
+ printf("_etext=%p\n", &_etext);
+ printf("_heap_start=%p\n", &_heap_start);
+ }
+
+ // GC info
+ {
+ 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);
+ }
+
+#if 0
+ // free space on flash
+ {
+ DWORD nclst;
+ FATFS *fatfs;
+ f_getfree("0:", &nclst, &fatfs);
+ printf("LFS free: %u bytes\n", (uint)(nclst * fatfs->csize * 512));
+ }
+#endif
+
+ return mp_const_none;
+}
+
+#define RAM_START (0x1FFF8000) // fixed for chip
+#define HEAP_END (0x20006000) // tunable
+#define RAM_END (0x20008000) // fixed for chip
+
+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;
+}
+
+mp_obj_t pyb_gpio(int n_args, mp_obj_t *args) {
+ //assert(1 <= n_args && n_args <= 2);
+
+ uint pin = mp_obj_get_int(args[0]);
+ if (pin > CORE_NUM_DIGITAL) {
+ goto pin_error;
+ }
+
+ if (n_args == 1) {
+ // get pin
+ pinMode(pin, INPUT);
+ return MP_OBJ_NEW_SMALL_INT(digitalRead(pin));
+ }
+
+ // set pin
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, rt_is_true(args[1]));
+ return mp_const_none;
+
+pin_error:
+ nlr_jump(mp_obj_new_exception_msg_1_arg(MP_QSTR_ValueError, "pin %d does not exist", (void *)(machine_uint_t)pin));
+}
+
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_gpio_obj, 1, 2, pyb_gpio);
+
+#if 0
+mp_obj_t pyb_hid_send_report(mp_obj_t arg) {
+ mp_obj_t *items = mp_obj_get_array_fixed_n(arg, 4);
+ 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);
+ return mp_const_none;
+}
+#endif
+
+mp_obj_t pyb_delay(mp_obj_t count) {
+ delay(mp_obj_get_int(count));
+ return mp_const_none;
+}
+
+mp_obj_t pyb_led(mp_obj_t state) {
+ led_state(PYB_LED_BUILTIN, rt_is_true(state));
+ return state;
+}
+
+char *strdup(const char *str) {
+ uint32_t len = strlen(str);
+ char *s2 = m_new(char, len + 1);
+ memcpy(s2, str, len);
+ 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);
+ }
+}
+
+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");
+
+ vstr_t line;
+ vstr_init(&line);
+
+ for (;;) {
+ vstr_reset(&line);
+ int ret = readline(&line, ">>> ");
+ if (ret == 0) {
+ // EOF
+ break;
+ }
+
+ if (vstr_len(&line) == 0) {
+ continue;
+ }
+
+ if (mp_repl_is_compound_stmt(vstr_str(&line))) {
+ for (;;) {
+ vstr_add_char(&line, '\n');
+ int len = vstr_len(&line);
+ int ret = readline(&line, "... ");
+ if (ret == 0 || vstr_len(&line) == len) {
+ // done entering compound statement
+ break;
+ }
+ }
+ }
+
+ mp_lexer_str_buf_t sb;
+ mp_lexer_t *lex = mp_lexer_new_from_str_len("<stdin>", vstr_str(&line), vstr_len(&line), false, &sb);
+ mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT);
+ mp_lexer_free(lex);
+
+ if (pn != MP_PARSE_NODE_NULL) {
+ mp_obj_t module_fun = mp_compile(pn, true);
+ if (module_fun != mp_const_none) {
+ nlr_buf_t nlr;
+ uint32_t start = micros();
+ if (nlr_push(&nlr) == 0) {
+ rt_call_function_0(module_fun);
+ nlr_pop();
+ // optional timing
+ if (0) {
+ uint32_t ticks = micros() - start; // TODO implement a function that does this properly
+ printf("(took %lu us)\n", ticks);
+ }
+ } else {
+ // uncaught exception
+ mp_obj_print((mp_obj_t)nlr.ret_val);
+ printf("\n");
+ }
+ }
+ }
+ }
+
+ stdout_tx_str("\r\n");
+}
+
+void main(void) {
+ pinMode(LED_BUILTIN, OUTPUT);
+ // Wait for host side to get connected
+ while (!usb_vcp_is_connected()) {
+ ;
+ }
+
+ led_init();
+ led_state(PYB_LED_BUILTIN, 1);
+
+// int first_soft_reset = true;
+
+soft_reset:
+
+ // GC init
+ gc_init(&_heap_start, (void*)HEAP_END);
+
+ qstr_init();
+ 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("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);
+ }
+#endif
+
+ // Turn bootup LED off
+ led_state(PYB_LED_BUILTIN, 0);
+
+ do_repl();
+
+ printf("PYB: soft reboot\n");
+
+// first_soft_reset = false;
+ goto soft_reset;
+}
+
+double __aeabi_f2d(float x) {
+ // TODO
+ return 0.0;
+}
+
+float __aeabi_d2f(double x) {
+ // TODO
+ return 0.0;
+}
+
+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) {
+}
+
+char * ultoa(unsigned long val, char *buf, int radix)
+{
+ unsigned digit;
+ int i=0, j;
+ char t;
+
+ while (1) {
+ digit = val % radix;
+ buf[i] = ((digit < 10) ? '0' + digit : 'A' + digit - 10);
+ val /= radix;
+ if (val == 0) break;
+ i++;
+ }
+ buf[i + 1] = 0;
+ for (j=0; j < i; j++, i--) {
+ t = buf[j];
+ buf[j] = buf[i];
+ buf[i] = t;
+ }
+ return buf;
+}
diff --git a/teensy/main.cpp b/teensy/main.cpp
new file mode 100644
index 0000000000..52ce51cda2
--- /dev/null
+++ b/teensy/main.cpp
@@ -0,0 +1,245 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "Arduino.h"
+
+extern "C"
+{
+#include "nlr.h"
+#include "misc.h"
+#include "mpconfig.h"
+#include "lexer.h"
+#include "lexerteensy.h"
+#include "parse.h"
+#include "obj.h"
+#include "compile.h"
+#include "runtime0.h"
+#include "runtime.h"
+#include "repl.h"
+#include "usb.h"
+}
+
+#ifdef USE_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#if 0
+static char *str_join(const char *s1, int sep_char, const char *s2) {
+ int l1 = strlen(s1);
+ int l2 = strlen(s2);
+ char *s = m_new(char, l1 + l2 + 2);
+ memcpy(s, s1, l1);
+ if (sep_char != 0) {
+ s[l1] = sep_char;
+ l1 += 1;
+ }
+ memcpy(s + l1, s2, l2);
+ s[l1 + l2] = 0;
+ return s;
+}
+
+static char *prompt(char *p) {
+#ifdef USE_READLINE
+ char *line = readline(p);
+ if (line) {
+ add_history(line);
+ }
+#else
+ static char buf[256];
+ fputs(p, stdout);
+ char *s = fgets(buf, sizeof(buf), stdin);
+ if (!s) {
+ return NULL;
+ }
+ int l = strlen(buf);
+ if (buf[l - 1] == '\n') {
+ buf[l - 1] = 0;
+ } else {
+ l++;
+ }
+ char *line = m_new(char, l);
+ memcpy(line, buf, l);
+#endif
+ return line;
+}
+#endif
+
+#define READLINE_HIST_SIZE (8)
+
+static const char *readline_hist[READLINE_HIST_SIZE] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+void stdout_tx_str(const char *str) {
+// usart_tx_str(str);
+ usb_vcp_send_str(str);
+}
+
+static elapsedMillis ledTime;
+static uint8_t ledState;
+
+int readline(vstr_t *line, const char *prompt) {
+ stdout_tx_str(prompt);
+ int len = vstr_len(line);
+ int escape = 0;
+ int hist_num = 0;
+ for (;;) {
+ char c;
+ ledState = 1;
+ ledTime = 0;
+ digitalWrite(LED_BUILTIN, ledState);
+ for (;;) {
+ if (ledTime > 200) {
+ ledState = !ledState;
+ digitalWrite(LED_BUILTIN, ledState);
+ ledTime = 0;
+ }
+ if (usb_vcp_rx_any() != 0) {
+ c = usb_vcp_rx_get();
+ break;
+#if 0
+ } else if (usart_rx_any()) {
+ c = usart_rx_char();
+ break;
+#endif
+ }
+ //delay(1);
+ //if (storage_needs_flush()) {
+ // storage_flush();
+ //}
+ }
+ if (escape == 0) {
+ if (c == 4 && vstr_len(line) == len) {
+ return 0;
+ } else if (c == '\r') {
+ stdout_tx_str("\r\n");
+ for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) {
+ readline_hist[i] = readline_hist[i - 1];
+ }
+ readline_hist[0] = strdup(vstr_str(line));
+ return 1;
+ } else if (c == 27) {
+ escape = true;
+ } else if (c == 127) {
+ if (vstr_len(line) > len) {
+ vstr_cut_tail(line, 1);
+ stdout_tx_str("\b \b");
+ }
+ } else if (32 <= c && c <= 126) {
+ vstr_add_char(line, c);
+ stdout_tx_str(line->buf + line->len - 1);
+ }
+ } else if (escape == 1) {
+ if (c == '[') {
+ escape = 2;
+ } else {
+ escape = 0;
+ }
+ } else if (escape == 2) {
+ escape = 0;
+ if (c == 'A') {
+ // up arrow
+ if (hist_num < READLINE_HIST_SIZE && readline_hist[hist_num] != NULL) {
+ // erase line
+ for (int i = line->len - len; i > 0; i--) {
+ stdout_tx_str("\b \b");
+ }
+ // set line to history
+ line->len = len;
+ vstr_add_str(line, readline_hist[hist_num]);
+ // draw line
+ stdout_tx_str(readline_hist[hist_num]);
+ // increase hist num
+ hist_num += 1;
+ }
+ }
+ } else {
+ escape = 0;
+ }
+ delay(10);
+ }
+}
+
+void setup(void) {
+ pinMode(LED_BUILTIN, OUTPUT);
+ ledState = 1;
+ digitalWrite(LED_BUILTIN, ledState);
+ ledTime = 0;
+ // Wait for host side to get connected
+ while (!usb_vcp_is_connected()) {
+ if (ledTime > 100) {
+ ledState = !ledState;
+ digitalWrite(LED_BUILTIN, ledState);
+ ledTime = 0;
+ }
+ }
+ digitalWrite(LED_BUILTIN, 0);
+
+ qstr_init();
+ rt_init();
+
+ stdout_tx_str("Micro Python for Teensy 3.1\r\n");
+ stdout_tx_str("Type \"help()\" for more information.\r\n");
+}
+
+void loop(void) {
+ vstr_t line;
+ vstr_init(&line);
+
+ vstr_reset(&line);
+ int ret = readline(&line, ">>> ");
+ if (ret == 0) {
+ // EOF
+ return;
+ }
+
+ if (vstr_len(&line) == 0) {
+ return;
+ }
+
+ if (mp_repl_is_compound_stmt(vstr_str(&line))) {
+ for (;;) {
+ vstr_add_char(&line, '\n');
+ int len = vstr_len(&line);
+ int ret = readline(&line, "... ");
+ if (ret == 0 || vstr_len(&line) == len) {
+ // done entering compound statement
+ break;
+ }
+ }
+ }
+
+ mp_lexer_str_buf_t sb;
+ mp_lexer_t *lex = mp_lexer_new_from_str_len("<stdin>", vstr_str(&line), vstr_len(&line), false, &sb);
+ mp_parse_node_t pn = mp_parse(lex, MP_PARSE_SINGLE_INPUT);
+ mp_lexer_free(lex);
+
+ if (pn != MP_PARSE_NODE_NULL) {
+ mp_obj_t module_fun = mp_compile(pn, true);
+ if (module_fun != mp_const_none) {
+ nlr_buf_t nlr;
+ uint32_t start = micros();
+ if (nlr_push(&nlr) == 0) {
+ rt_call_function_0(module_fun);
+ nlr_pop();
+ // optional timing
+ if (0) {
+ uint32_t ticks = micros() - start; // TODO implement a function that does this properly
+ printf("(took %lu us)\n", ticks);
+ }
+ } else {
+ // uncaught exception
+ mp_obj_print((mp_obj_t)nlr.ret_val);
+ printf("\r\n");
+ }
+ }
+ }
+}
+
+// for sqrt
+#include <math.h>
+machine_float_t machine_sqrt(machine_float_t x) {
+ return sqrt(x);
+}
+
diff --git a/teensy/malloc0.c b/teensy/malloc0.c
new file mode 100644
index 0000000000..1a6cb708e8
--- /dev/null
+++ b/teensy/malloc0.c
@@ -0,0 +1,57 @@
+#include <stdint.h>
+#include "std.h"
+#include "mpconfig.h"
+#include "gc.h"
+
+#if 0
+static uint32_t mem = 0;
+
+void *malloc(size_t n) {
+ if (mem == 0) {
+ extern uint32_t _heap_start;
+ mem = (uint32_t)&_heap_start; // need to use big ram block so we can execute code from it (is it true that we can't execute from CCM?)
+ }
+ void *ptr = (void*)mem;
+ mem = (mem + n + 3) & (~3);
+ if (mem > 0x20000000 + 0x18000) {
+ void __fatal_error(const char*);
+ __fatal_error("out of memory");
+ }
+ return ptr;
+}
+
+void free(void *ptr) {
+}
+
+void *realloc(void *ptr, size_t n) {
+ return malloc(n);
+}
+
+#endif
+
+void *calloc(size_t sz, size_t n) {
+ char *ptr = malloc(sz * n);
+ for (int i = 0; i < sz * n; i++) {
+ ptr[i] = 0;
+ }
+ return ptr;
+}
+
+void *malloc(size_t n) {
+ void *m = gc_alloc(n);
+ return m;
+}
+
+void free(void *ptr) {
+ gc_free(ptr);
+}
+
+void *realloc(void *ptr, size_t n) {
+ return gc_realloc(ptr, n);
+}
+
+void __assert_func(void) {
+ printf("\nASSERT FAIL!");
+ for (;;) {
+ }
+}
diff --git a/teensy/mk20dx256.ld b/teensy/mk20dx256.ld
new file mode 100644
index 0000000000..da57cbe5cb
--- /dev/null
+++ b/teensy/mk20dx256.ld
@@ -0,0 +1,164 @@
+/* Teensyduino Core Library
+ * http://www.pjrc.com/teensy/
+ * Copyright (c) 2013 PJRC.COM, LLC.
+ *
+ * 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:
+ *
+ * 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
+ * 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.
+ *
+ * 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.
+ */
+
+MEMORY
+{
+ 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 */
+_minimum_stack_size = 2K;
+_minimum_heap_size = 16K;
+
+/* INCLUDE common.ld */
+
+/* Teensyduino Core Library
+ * http://www.pjrc.com/teensy/
+ * Copyright (c) 2013 PJRC.COM, LLC.
+ *
+ * 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:
+ *
+ * 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
+ * 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.
+ *
+ * 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.
+ */
+
+
+
+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
+
+ .ARM.exidx : {
+ __exidx_start = .;
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ __exidx_end = .;
+ } > FLASH
+ _etext = .;
+
+ .usbdescriptortable (NOLOAD) : {
+ /* . = ORIGIN(RAM); */
+ . = ALIGN(512);
+ *(.usbdescriptortable*)
+ } > RAM
+
+ .dmabuffers (NOLOAD) : {
+ . = ALIGN(4);
+ *(.dmabuffers*)
+ } > RAM
+
+ .usbbuffers (NOLOAD) : {
+ . = ALIGN(4);
+ *(.usbbuffers*)
+ } > RAM
+
+ .data : AT (_etext) {
+ . = ALIGN(4);
+ _sdata = .;
+ *(.data*)
+ . = ALIGN(4);
+ _edata = .;
+ } > RAM
+
+ .noinit (NOLOAD) : {
+ *(.noinit*)
+ } > RAM
+
+ .bss : {
+ . = ALIGN(4);
+ _sbss = .;
+ *(.bss*)
+ *(COMMON)
+ . = ALIGN(4);
+ _ebss = .;
+ __bss_end = .;
+ } > 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
+
+ /* this just checks there is enough RAM for the stack */
+ .stack :
+ {
+ . = ALIGN(4);
+ . = . + _minimum_stack_size;
+ . = ALIGN(4);
+ } >RAM
+
+ _estack = ORIGIN(RAM) + LENGTH(RAM);
+}
+
+
+
+
diff --git a/teensy/mpconfigport.h b/teensy/mpconfigport.h
new file mode 100644
index 0000000000..4cea332f39
--- /dev/null
+++ b/teensy/mpconfigport.h
@@ -0,0 +1,21 @@
+#include <stdint.h>
+
+// options to control how Micro Python is built
+
+#define MICROPY_ENABLE_FLOAT (1)
+#define MICROPY_EMIT_CPYTHON (0)
+#define MICROPY_EMIT_X64 (0)
+#define MICROPY_EMIT_THUMB (1)
+#define MICROPY_EMIT_INLINE_THUMB (1)
+
+// type definitions for the specific machine
+
+#define BYTES_PER_WORD (4)
+
+typedef int32_t machine_int_t; // must be pointer size
+typedef uint32_t 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);
diff --git a/teensy/printf.c b/teensy/printf.c
new file mode 100644
index 0000000000..71b27b8214
--- /dev/null
+++ b/teensy/printf.c
@@ -0,0 +1,305 @@
+#include <stdint.h>
+#include <stdarg.h>
+#include "std.h"
+#include "misc.h"
+//#include "lcd.h"
+//#include "usart.h"
+#include "usb.h"
+
+#define PF_FLAG_LEFT_ADJUST (0x01)
+#define PF_FLAG_SHOW_SIGN (0x02)
+#define PF_FLAG_SPACE_SIGN (0x04)
+#define PF_FLAG_NO_TRAILZ (0x08)
+#define PF_FLAG_ZERO_PAD (0x10)
+
+// tricky; we compute pad string by: pad_chars + (flags & PF_FLAG_ZERO_PAD)
+#define PF_PAD_SIZE PF_FLAG_ZERO_PAD
+static const char *pad_chars = " 0000000000000000";
+
+typedef struct _pfenv_t {
+ void *data;
+ void (*print_strn)(void *, const char *str, unsigned int len);
+} pfenv_t;
+
+static void print_str_dummy(void *data, const char *str, unsigned int len) {
+}
+
+const pfenv_t pfenv_dummy = {0, print_str_dummy};
+
+static int pfenv_print_strn(const pfenv_t *pfenv, const char *str, unsigned int len, int flags, int width) {
+ int pad = width - len;
+ if (pad > 0 && (flags & PF_FLAG_LEFT_ADJUST) == 0) {
+ while (pad > 0) {
+ int p = pad;
+ if (p > PF_PAD_SIZE)
+ p = PF_PAD_SIZE;
+ pfenv->print_strn(pfenv->data, pad_chars + (flags & PF_FLAG_ZERO_PAD), p);
+ pad -= p;
+ }
+ }
+ pfenv->print_strn(pfenv->data, str, len);
+ while (pad > 0) {
+ int p = pad;
+ if (p > PF_PAD_SIZE)
+ p = PF_PAD_SIZE;
+ pfenv->print_strn(pfenv->data, pad_chars, p);
+ pad -= p;
+ }
+ return len;
+}
+
+// enough room for 32 signed number
+#define INT_BUF_SIZE (12)
+
+static int pfenv_print_int(const pfenv_t *pfenv, unsigned int x, int sgn, int base, int base_char, int flags, int width) {
+ char sign = 0;
+ if (sgn) {
+ if ((int)x < 0) {
+ sign = '-';
+ x = -x;
+ } else if (flags & PF_FLAG_SHOW_SIGN) {
+ sign = '+';
+ } else if (flags & PF_FLAG_SPACE_SIGN) {
+ sign = ' ';
+ }
+ }
+
+ char buf[INT_BUF_SIZE];
+ char *b = buf + INT_BUF_SIZE;
+
+ if (x == 0) {
+ *(--b) = '0';
+ } else {
+ do {
+ int c = x % base;
+ x /= base;
+ if (c >= 10) {
+ c += base_char - 10;
+ } else {
+ c += '0';
+ }
+ *(--b) = c;
+ } while (b > buf && x != 0);
+ }
+
+ if (b > buf && sign != 0) {
+ *(--b) = sign;
+ }
+
+ return pfenv_print_strn(pfenv, b, buf + INT_BUF_SIZE - b, flags, width);
+}
+
+void pfenv_prints(const pfenv_t *pfenv, const char *str) {
+ pfenv->print_strn(pfenv->data, str, strlen(str));
+}
+
+int pfenv_printf(const pfenv_t *pfenv, const char *fmt, va_list args) {
+ int chrs = 0;
+ for (;;) {
+ {
+ const char *f = fmt;
+ while (*f != '\0' && *f != '%') {
+ ++f; // XXX UTF8 advance char
+ }
+ if (f > fmt) {
+ pfenv->print_strn(pfenv->data, fmt, f - fmt);
+ chrs += f - fmt;
+ fmt = f;
+ }
+ }
+
+ if (*fmt == '\0') {
+ break;
+ }
+
+ // move past % character
+ ++fmt;
+
+ // parse flags, if they exist
+ int flags = 0;
+ while (*fmt != '\0') {
+ if (*fmt == '-') flags |= PF_FLAG_LEFT_ADJUST;
+ else if (*fmt == '+') flags |= PF_FLAG_SHOW_SIGN;
+ else if (*fmt == ' ') flags |= PF_FLAG_SPACE_SIGN;
+ else if (*fmt == '!') flags |= PF_FLAG_NO_TRAILZ;
+ else if (*fmt == '0') flags |= PF_FLAG_ZERO_PAD;
+ else break;
+ ++fmt;
+ }
+
+ // parse width, if it exists
+ int width = 0;
+ for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
+ width = width * 10 + *fmt - '0';
+ }
+
+ // parse precision, if it exists
+ int prec = -1;
+ if (*fmt == '.') {
+ ++fmt;
+ if (*fmt == '*') {
+ ++fmt;
+ prec = va_arg(args, int);
+ } else {
+ prec = 0;
+ for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
+ prec = prec * 10 + *fmt - '0';
+ }
+ }
+ if (prec < 0) {
+ prec = 0;
+ }
+ }
+
+ // parse long specifiers (current not used)
+ //bool long_arg = false;
+ if (*fmt == 'l') {
+ ++fmt;
+ //long_arg = true;
+ }
+
+ if (*fmt == '\0') {
+ break;
+ }
+
+ switch (*fmt) {
+ case 'b':
+ if (va_arg(args, int)) {
+ chrs += pfenv_print_strn(pfenv, "true", 4, flags, width);
+ } else {
+ chrs += pfenv_print_strn(pfenv, "false", 5, flags, width);
+ }
+ break;
+ case 'c':
+ {
+ char str = va_arg(args, int);
+ chrs += pfenv_print_strn(pfenv, &str, 1, flags, width);
+ break;
+ }
+ case 's':
+ {
+ const char *str = va_arg(args, const char*);
+ if (str) {
+ if (prec < 0) {
+ prec = strlen(str);
+ }
+ chrs += pfenv_print_strn(pfenv, str, prec, flags, width);
+ } else {
+ chrs += pfenv_print_strn(pfenv, "(null)", 6, flags, width);
+ }
+ break;
+ }
+ case 'u':
+ chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 10, 'a', flags, width);
+ break;
+ case 'd':
+ chrs += pfenv_print_int(pfenv, va_arg(args, int), 1, 10, 'a', flags, width);
+ break;
+ case 'x':
+ case 'p': // ?
+ chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'a', flags, width);
+ break;
+ case 'X':
+ case 'P': // ?
+ chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'A', flags, width);
+ break;
+ default:
+ pfenv->print_strn(pfenv->data, fmt, 1);
+ chrs += 1;
+ break;
+ }
+ ++fmt;
+ }
+ return chrs;
+}
+
+void stdout_print_strn(void *data, const char *str, unsigned int len) {
+ // send stdout to USART, USB CDC VCP, and LCD if nothing else
+#if 0
+ int any = 0;
+ if (usart_is_enabled()) {
+ usart_tx_strn_cooked(str, len);
+ any = true;
+ }
+#endif
+ if (usb_vcp_is_enabled()) {
+ usb_vcp_send_strn_cooked(str, len);
+// any = true;
+ }
+#if 0
+ if (!any) {
+ lcd_print_strn(str, len);
+ }
+#endif
+}
+
+static const pfenv_t pfenv_stdout = {0, stdout_print_strn};
+
+int printf(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ int ret = pfenv_printf(&pfenv_stdout, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+int vprintf(const char *fmt, va_list ap) {
+ return pfenv_printf(&pfenv_stdout, fmt, ap);
+}
+
+// need this because gcc optimises printf("%c", c) -> putchar(c), and printf("a") -> putchar('a')
+int putchar(int c) {
+ char chr = c;
+ stdout_print_strn(0, &chr, 1);
+ return chr;
+}
+
+// need this because gcc optimises printf("string\n") -> puts("string")
+int puts(const char *s) {
+ stdout_print_strn(0, s, strlen(s));
+ char chr = '\n';
+ stdout_print_strn(0, &chr, 1);
+ return 1;
+}
+
+typedef struct _strn_pfenv_t {
+ char *cur;
+ size_t remain;
+} strn_pfenv_t;
+
+void strn_print_strn(void *data, const char *str, unsigned int len) {
+ strn_pfenv_t *strn_pfenv = data;
+ if (len > strn_pfenv->remain) {
+ len = strn_pfenv->remain;
+ }
+ memcpy(strn_pfenv->cur, str, len);
+ strn_pfenv->cur += len;
+ strn_pfenv->remain -= len;
+}
+
+int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) {
+ strn_pfenv_t strn_pfenv;
+ strn_pfenv.cur = str;
+ strn_pfenv.remain = size;
+ pfenv_t pfenv;
+ pfenv.data = &strn_pfenv;
+ pfenv.print_strn = strn_print_strn;
+ int len = pfenv_printf(&pfenv, fmt, ap);
+ // add terminating null byte
+ if (size > 0) {
+ if (strn_pfenv.remain == 0) {
+ strn_pfenv.cur[-1] = 0;
+ } else {
+ strn_pfenv.cur[0] = 0;
+ }
+ }
+ return len;
+}
+
+int snprintf(char *str, size_t size, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ int ret = vsnprintf(str, size, fmt, ap);
+ va_end(ap);
+ return ret;
+}
diff --git a/teensy/std.h b/teensy/std.h
new file mode 100644
index 0000000000..587b9f8880
--- /dev/null
+++ b/teensy/std.h
@@ -0,0 +1,22 @@
+typedef unsigned int size_t;
+
+void __assert_func(void);
+
+void *malloc(size_t n);
+void free(void *ptr);
+void *calloc(size_t sz, size_t n);
+void *realloc(void *ptr, size_t n);
+
+void *memcpy(void *dest, const void *src, size_t n);
+void *memmove(void *dest, const void *src, size_t n);
+void *memset(void *s, int c, size_t n);
+
+int strlen(const char *str);
+int strcmp(const char *s1, const char *s2);
+int strncmp(const char *s1, const char *s2, size_t n);
+char *strndup(const char *s, size_t n);
+char *strcpy(char *dest, const char *src);
+char *strcat(char *dest, const char *src);
+
+int printf(const char *fmt, ...);
+int snprintf(char *str, size_t size, const char *fmt, ...);
diff --git a/teensy/string0.c b/teensy/string0.c
new file mode 100644
index 0000000000..2a5f255971
--- /dev/null
+++ b/teensy/string0.c
@@ -0,0 +1,110 @@
+#include <stdint.h>
+#include "std.h"
+
+void *memcpy(void *dest, const void *src, size_t n) {
+ // TODO align and copy 32 bits at a time
+ uint8_t *d = dest;
+ const uint8_t *s = src;
+ for (; n > 0; n--) {
+ *d++ = *s++;
+ }
+ return dest;
+}
+
+void *memmove(void *dest, const void *src, size_t n) {
+ if (src < dest && dest < src + n) {
+ // need to copy backwards
+ uint8_t *d = dest + n - 1;
+ const uint8_t *s = src + n - 1;
+ for (; n > 0; n--) {
+ *d-- = *s--;
+ }
+ return dest;
+ } else {
+ // can use normal memcpy
+ return memcpy(dest, src, n);
+ }
+}
+
+void *memset(void *s, int c, size_t n) {
+ uint8_t *s2 = s;
+ for (; n > 0; n--) {
+ *s2++ = c;
+ }
+ return s;
+}
+
+int strlen(const char *str) {
+ int len = 0;
+ for (const char *s = str; *s; s++) {
+ len += 1;
+ }
+ return len;
+}
+
+int strcmp(const char *s1, const char *s2) {
+ while (*s1 && *s2) {
+ char c1 = *s1++; // XXX UTF8 get char, next char
+ char c2 = *s2++; // XXX UTF8 get char, next char
+ if (c1 < c2) return -1;
+ else if (c1 > c2) return 1;
+ }
+ if (*s2) return -1;
+ else if (*s1) return 1;
+ else return 0;
+}
+
+int strncmp(const char *s1, const char *s2, size_t n) {
+ while (*s1 && *s2 && n > 0) {
+ char c1 = *s1++; // XXX UTF8 get char, next char
+ char c2 = *s2++; // XXX UTF8 get char, next char
+ n--;
+ if (c1 < c2) return -1;
+ else if (c1 > c2) return 1;
+ }
+ if (n == 0) return 0;
+ else if (*s2) return -1;
+ else if (*s1) return 1;
+ else return 0;
+}
+
+char *strndup(const char *s, size_t n) {
+ size_t len = strlen(s);
+ if (n > len) {
+ n = len;
+ }
+ char *s2 = malloc(n + 1);
+ memcpy(s2, s, n);
+ s2[n] = '\0';
+ return s2;
+}
+
+char *strcpy(char *dest, const char *src) {
+ char *d = dest;
+ while (*src) {
+ *d++ = *src++;
+ }
+ *d = '\0';
+ return dest;
+}
+
+// needed because gcc optimises strcpy + strcat to this
+char *stpcpy(char *dest, const char *src) {
+ while (*src) {
+ *dest++ = *src++;
+ }
+ *dest = '\0';
+ return dest;
+}
+
+char *strcat(char *dest, const char *src) {
+ char *d = dest;
+ while (*d) {
+ d++;
+ }
+ while (*src) {
+ *d++ = *src++;
+ }
+ *d = '\0';
+ return dest;
+}
diff --git a/teensy/usb.c b/teensy/usb.c
new file mode 100644
index 0000000000..a045a2ed69
--- /dev/null
+++ b/teensy/usb.c
@@ -0,0 +1,44 @@
+#include "Arduino.h"
+
+#include "usb.h"
+#include "usb_serial.h"
+
+int usb_vcp_is_connected(void)
+{
+ return usb_configuration && (usb_cdc_line_rtsdtr & (USB_SERIAL_DTR | USB_SERIAL_RTS));
+}
+
+int usb_vcp_is_enabled(void)
+{
+ return 1;
+}
+
+int usb_vcp_rx_any(void)
+{
+ return usb_serial_available();
+}
+
+char usb_vcp_rx_get(void)
+{
+ return usb_serial_getchar();
+}
+
+void usb_vcp_send_str(const char* str)
+{
+ usb_vcp_send_strn(str, strlen(str));
+}
+
+void usb_vcp_send_strn(const char* str, int len)
+{
+ usb_serial_write(str, len);
+}
+
+void usb_vcp_send_strn_cooked(const char *str, int len)
+{
+ for (const char *top = str + len; str < top; str++) {
+ if (*str == '\n') {
+ usb_serial_putchar('\r');
+ }
+ usb_serial_putchar(*str);
+ }
+}
diff --git a/teensy/usb.h b/teensy/usb.h
new file mode 100644
index 0000000000..8f32309ecb
--- /dev/null
+++ b/teensy/usb.h
@@ -0,0 +1,9 @@
+void usb_init(void);
+int usb_vcp_is_enabled(void);
+int usb_vcp_is_connected(void);
+int usb_vcp_rx_any(void);
+char usb_vcp_rx_get(void);
+void usb_vcp_send_str(const char* str);
+void usb_vcp_send_strn(const char* str, int len);
+void usb_vcp_send_strn_cooked(const char *str, int len);
+void usb_hid_send_report(uint8_t *buf); // 4 bytes for mouse: ?, x, y, ?