97790455fe
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.
572 lines
16 KiB
C
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;
|
|
}
|