circuitpython/teensy/main.c
Damien George 97790455fe Improve REPL detecting when input needs to continue.
Full CPython compatibility with this requires actually parsing the
input so far collected, and if it fails parsing due to lack of tokens,
then continue collecting input.  It's not worth doing it this way.  Not
having compatibility at this level does not hurt the goals of Micro
Python.
2014-04-08 11:04:29 +00:00

572 lines
16 KiB
C

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "nlr.h"
#include "misc.h"
#include "mpconfig.h"
#include "qstr.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 "Arduino.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);
delay(250);
led_state(PYB_LED_BUILTIN, 0);
delay(250);
}
}
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
;
mp_obj_t pyb_analog_read(mp_obj_t pin_obj) {
uint pin = mp_obj_get_int(pin_obj);
int val = analogRead(pin);
return MP_OBJ_NEW_SMALL_INT(val);
}
mp_obj_t pyb_analog_write(mp_obj_t pin_obj, mp_obj_t val_obj) {
uint pin = mp_obj_get_int(pin_obj);
int val = mp_obj_get_int(val_obj);
analogWrite(pin, val);
return mp_const_none;
}
mp_obj_t pyb_analog_write_resolution(mp_obj_t res_obj) {
int res = mp_obj_get_int(res_obj);
analogWriteResolution(res);
return mp_const_none;
}
mp_obj_t pyb_analog_write_frequency(mp_obj_t pin_obj, mp_obj_t freq_obj) {
uint pin = mp_obj_get_int(pin_obj);
int freq = mp_obj_get_int(freq_obj);
analogWriteFrequency(pin, freq);
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;
}
// 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, mp_obj_is_true(args[1]));
return mp_const_none;
pin_error:
nlr_raise(mp_obj_new_exception_msg_varg(MP_QSTR_ValueError, "pin %d does not exist", 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
static mp_obj_t pyb_config_source_dir = MP_OBJ_NULL;
static mp_obj_t pyb_config_main = 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_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_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, mp_obj_is_true(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;
}
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);
}
}
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:
// GC init
gc_init(&_heap_start, (void*)HEAP_END);
qstr_init();
mp_init();
// add some functions to the python namespace
{
mp_store_name(MP_QSTR_help, mp_make_function_n(0, pyb_help));
mp_obj_t m = mp_obj_new_module(MP_QSTR_pyb);
mp_store_attr(m, MP_QSTR_info, mp_make_function_n(0, pyb_info));
mp_store_attr(m, MP_QSTR_source_dir, mp_make_function_n(1, pyb_source_dir));
mp_store_attr(m, MP_QSTR_main, mp_make_function_n(1, pyb_main));
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_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));
mp_store_attr(m, MP_QSTR_analogWriteFrequency, mp_make_function_n(2, pyb_analog_write_frequency));
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));
}
printf("About execute /boot.py\n");
if (!do_file("/boot.py")) {
printf("Unable to open '/boot.py'\n");
flash_error(4);
}
printf("Done executing /boot.py\n");
// 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 == 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));
flash_error(3);
}
printf("Done executing '%s'\n", vstr_str(vstr));
vstr_free(vstr);
}
do_repl();
printf("PYB: soft reboot\n");
// first_soft_reset = false;
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) {
}
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;
}