micropython: add micropython component
This commit is contained in:
50
components/language/micropython/shared/runtime/gchelper.h
Normal file
50
components/language/micropython/shared/runtime/gchelper.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 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.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_LIB_UTILS_GCHELPER_H
|
||||
#define MICROPY_INCLUDED_LIB_UTILS_GCHELPER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if MICROPY_GCREGS_SETJMP
|
||||
#include <setjmp.h>
|
||||
typedef jmp_buf gc_helper_regs_t;
|
||||
#else
|
||||
|
||||
#if defined(__x86_64__)
|
||||
typedef uintptr_t gc_helper_regs_t[6];
|
||||
#elif defined(__i386__)
|
||||
typedef uintptr_t gc_helper_regs_t[4];
|
||||
#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__)
|
||||
typedef uintptr_t gc_helper_regs_t[10];
|
||||
#elif defined(__aarch64__)
|
||||
typedef uintptr_t gc_helper_regs_t[11]; // x19-x29
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
void gc_helper_collect_regs_and_stack(void);
|
||||
|
||||
#endif // MICROPY_INCLUDED_LIB_UTILS_GCHELPER_H
|
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* This file is part of the MicroPython 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 "py/mpstate.h"
|
||||
#include "py/gc.h"
|
||||
#include "shared/runtime/gchelper.h"
|
||||
|
||||
#if MICROPY_ENABLE_GC
|
||||
|
||||
// Even if we have specific support for an architecture, it is
|
||||
// possible to force use of setjmp-based implementation.
|
||||
#if !MICROPY_GCREGS_SETJMP
|
||||
|
||||
// We capture here callee-save registers, i.e. ones which may contain
|
||||
// interesting values held there by our callers. It doesn't make sense
|
||||
// to capture caller-saved registers, because they, well, put on the
|
||||
// stack already by the caller.
|
||||
#if defined(__x86_64__)
|
||||
|
||||
STATIC void gc_helper_get_regs(gc_helper_regs_t arr) {
|
||||
register long rbx asm ("rbx");
|
||||
register long rbp asm ("rbp");
|
||||
register long r12 asm ("r12");
|
||||
register long r13 asm ("r13");
|
||||
register long r14 asm ("r14");
|
||||
register long r15 asm ("r15");
|
||||
#ifdef __clang__
|
||||
// TODO:
|
||||
// This is dirty workaround for Clang. It tries to get around
|
||||
// uncompliant (wrt to GCC) behavior of handling register variables.
|
||||
// Application of this patch here is random, and done only to unbreak
|
||||
// MacOS build. Better, cross-arch ways to deal with Clang issues should
|
||||
// be found.
|
||||
asm ("" : "=r" (rbx));
|
||||
asm ("" : "=r" (rbp));
|
||||
asm ("" : "=r" (r12));
|
||||
asm ("" : "=r" (r13));
|
||||
asm ("" : "=r" (r14));
|
||||
asm ("" : "=r" (r15));
|
||||
#endif
|
||||
arr[0] = rbx;
|
||||
arr[1] = rbp;
|
||||
arr[2] = r12;
|
||||
arr[3] = r13;
|
||||
arr[4] = r14;
|
||||
arr[5] = r15;
|
||||
}
|
||||
|
||||
#elif defined(__i386__)
|
||||
|
||||
STATIC void gc_helper_get_regs(gc_helper_regs_t arr) {
|
||||
register long ebx asm ("ebx");
|
||||
register long esi asm ("esi");
|
||||
register long edi asm ("edi");
|
||||
register long ebp asm ("ebp");
|
||||
#ifdef __clang__
|
||||
// TODO:
|
||||
// This is dirty workaround for Clang. It tries to get around
|
||||
// uncompliant (wrt to GCC) behavior of handling register variables.
|
||||
// Application of this patch here is random, and done only to unbreak
|
||||
// MacOS build. Better, cross-arch ways to deal with Clang issues should
|
||||
// be found.
|
||||
asm ("" : "=r" (ebx));
|
||||
asm ("" : "=r" (esi));
|
||||
asm ("" : "=r" (edi));
|
||||
asm ("" : "=r" (ebp));
|
||||
#endif
|
||||
arr[0] = ebx;
|
||||
arr[1] = esi;
|
||||
arr[2] = edi;
|
||||
arr[3] = ebp;
|
||||
}
|
||||
|
||||
#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__)
|
||||
|
||||
// Fallback implementation, prefer gchelper_m0.s or gchelper_m3.s
|
||||
|
||||
STATIC void gc_helper_get_regs(gc_helper_regs_t arr) {
|
||||
register long r4 asm ("r4");
|
||||
register long r5 asm ("r5");
|
||||
register long r6 asm ("r6");
|
||||
register long r7 asm ("r7");
|
||||
register long r8 asm ("r8");
|
||||
register long r9 asm ("r9");
|
||||
register long r10 asm ("r10");
|
||||
register long r11 asm ("r11");
|
||||
register long r12 asm ("r12");
|
||||
register long r13 asm ("r13");
|
||||
arr[0] = r4;
|
||||
arr[1] = r5;
|
||||
arr[2] = r6;
|
||||
arr[3] = r7;
|
||||
arr[4] = r8;
|
||||
arr[5] = r9;
|
||||
arr[6] = r10;
|
||||
arr[7] = r11;
|
||||
arr[8] = r12;
|
||||
arr[9] = r13;
|
||||
}
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
STATIC void gc_helper_get_regs(gc_helper_regs_t arr) {
|
||||
const register long x19 asm ("x19");
|
||||
const register long x20 asm ("x20");
|
||||
const register long x21 asm ("x21");
|
||||
const register long x22 asm ("x22");
|
||||
const register long x23 asm ("x23");
|
||||
const register long x24 asm ("x24");
|
||||
const register long x25 asm ("x25");
|
||||
const register long x26 asm ("x26");
|
||||
const register long x27 asm ("x27");
|
||||
const register long x28 asm ("x28");
|
||||
const register long x29 asm ("x29");
|
||||
arr[0] = x19;
|
||||
arr[1] = x20;
|
||||
arr[2] = x21;
|
||||
arr[3] = x22;
|
||||
arr[4] = x23;
|
||||
arr[5] = x24;
|
||||
arr[6] = x25;
|
||||
arr[7] = x26;
|
||||
arr[8] = x27;
|
||||
arr[9] = x28;
|
||||
arr[10] = x29;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#error "Architecture not supported for gc_helper_get_regs. Set MICROPY_GCREGS_SETJMP to use the fallback implementation."
|
||||
|
||||
#endif
|
||||
|
||||
#else // !MICROPY_GCREGS_SETJMP
|
||||
|
||||
// Even if we have specific support for an architecture, it is
|
||||
// possible to force use of setjmp-based implementation.
|
||||
|
||||
STATIC void gc_helper_get_regs(gc_helper_regs_t arr) {
|
||||
setjmp(arr);
|
||||
}
|
||||
|
||||
#endif // MICROPY_GCREGS_SETJMP
|
||||
|
||||
// Explicitly mark this as noinline to make sure the regs variable
|
||||
// is effectively at the top of the stack: otherwise, in builds where
|
||||
// LTO is enabled and a lot of inlining takes place we risk a stack
|
||||
// layout where regs is lower on the stack than pointers which have
|
||||
// just been allocated but not yet marked, and get incorrectly sweeped.
|
||||
MP_NOINLINE void gc_helper_collect_regs_and_stack(void) {
|
||||
gc_helper_regs_t regs;
|
||||
gc_helper_get_regs(regs);
|
||||
// GC stack (and regs because we captured them)
|
||||
void **regs_ptr = (void **)(void *)®s;
|
||||
gc_collect_root(regs_ptr, ((uintptr_t)MP_STATE_THREAD(stack_top) - (uintptr_t)®s) / sizeof(uintptr_t));
|
||||
}
|
||||
|
||||
#endif // MICROPY_ENABLE_GC
|
61
components/language/micropython/shared/runtime/gchelper_m0.s
Normal file
61
components/language/micropython/shared/runtime/gchelper_m0.s
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 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.
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.cpu cortex-m0
|
||||
.thumb
|
||||
|
||||
.section .text
|
||||
.align 2
|
||||
|
||||
.global gc_helper_get_regs_and_sp
|
||||
.type gc_helper_get_regs_and_sp, %function
|
||||
|
||||
@ uint gc_helper_get_regs_and_sp(r0=uint regs[10])
|
||||
gc_helper_get_regs_and_sp:
|
||||
@ store registers into given array
|
||||
str r4, [r0, #0]
|
||||
str r5, [r0, #4]
|
||||
str r6, [r0, #8]
|
||||
str r7, [r0, #12]
|
||||
mov r1, r8
|
||||
str r1, [r0, #16]
|
||||
mov r1, r9
|
||||
str r1, [r0, #20]
|
||||
mov r1, r10
|
||||
str r1, [r0, #24]
|
||||
mov r1, r11
|
||||
str r1, [r0, #28]
|
||||
mov r1, r12
|
||||
str r1, [r0, #32]
|
||||
mov r1, r13
|
||||
str r1, [r0, #36]
|
||||
|
||||
@ return the sp
|
||||
mov r0, sp
|
||||
bx lr
|
||||
|
||||
.size gc_helper_get_regs_and_sp, .-gc_helper_get_regs_and_sp
|
55
components/language/micropython/shared/runtime/gchelper_m3.s
Normal file
55
components/language/micropython/shared/runtime/gchelper_m3.s
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* This file is part of the MicroPython 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.
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.cpu cortex-m3
|
||||
.thumb
|
||||
|
||||
.section .text
|
||||
.align 2
|
||||
|
||||
.global gc_helper_get_regs_and_sp
|
||||
.type gc_helper_get_regs_and_sp, %function
|
||||
|
||||
@ uint gc_helper_get_regs_and_sp(r0=uint regs[10])
|
||||
gc_helper_get_regs_and_sp:
|
||||
@ 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
|
||||
|
||||
@ return the sp
|
||||
mov r0, sp
|
||||
bx lr
|
||||
|
||||
.size gc_helper_get_regs_and_sp, .-gc_helper_get_regs_and_sp
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of the MicroPython 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 "py/mpstate.h"
|
||||
#include "py/gc.h"
|
||||
#include "shared/runtime/gchelper.h"
|
||||
|
||||
#if MICROPY_ENABLE_GC
|
||||
|
||||
// provided by gchelper_*.s
|
||||
uintptr_t gc_helper_get_regs_and_sp(uintptr_t *regs);
|
||||
|
||||
MP_NOINLINE void gc_helper_collect_regs_and_stack(void) {
|
||||
// get the registers and the sp
|
||||
gc_helper_regs_t regs;
|
||||
uintptr_t sp = gc_helper_get_regs_and_sp(regs);
|
||||
|
||||
// trace the stack, including the registers (since they live on the stack in this function)
|
||||
gc_collect_root((void **)sp, ((uint32_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t));
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2016 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 "py/obj.h"
|
||||
#include "py/mpstate.h"
|
||||
|
||||
#if MICROPY_KBD_EXCEPTION
|
||||
|
||||
int mp_interrupt_char = -1;
|
||||
|
||||
void mp_hal_set_interrupt_char(int c) {
|
||||
mp_interrupt_char = c;
|
||||
}
|
||||
|
||||
#endif
|
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2016 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.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H
|
||||
#define MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H
|
||||
|
||||
extern int mp_interrupt_char;
|
||||
void mp_hal_set_interrupt_char(int c);
|
||||
|
||||
#endif // MICROPY_INCLUDED_LIB_UTILS_INTERRUPT_CHAR_H
|
135
components/language/micropython/shared/runtime/mpirq.c
Normal file
135
components/language/micropython/shared/runtime/mpirq.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Daniel Campora
|
||||
* 2018 Tobias Badertscher
|
||||
*
|
||||
* 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 "py/runtime.h"
|
||||
#include "py/gc.h"
|
||||
#include "shared/runtime/mpirq.h"
|
||||
|
||||
#if MICROPY_ENABLE_SCHEDULER
|
||||
|
||||
/******************************************************************************
|
||||
DECLARE PUBLIC DATA
|
||||
******************************************************************************/
|
||||
|
||||
const mp_arg_t mp_irq_init_args[] = {
|
||||
{ MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
|
||||
{ MP_QSTR_trigger, MP_ARG_INT, {.u_int = 0} },
|
||||
{ MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} },
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
DECLARE PRIVATE DATA
|
||||
******************************************************************************/
|
||||
|
||||
/******************************************************************************
|
||||
DEFINE PUBLIC FUNCTIONS
|
||||
******************************************************************************/
|
||||
|
||||
mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent) {
|
||||
mp_irq_obj_t *self = m_new0(mp_irq_obj_t, 1);
|
||||
mp_irq_init(self, methods, parent);
|
||||
return self;
|
||||
}
|
||||
|
||||
void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent) {
|
||||
self->base.type = &mp_irq_type;
|
||||
self->methods = (mp_irq_methods_t *)methods;
|
||||
self->parent = parent;
|
||||
self->handler = mp_const_none;
|
||||
self->ishard = false;
|
||||
}
|
||||
|
||||
void mp_irq_handler(mp_irq_obj_t *self) {
|
||||
if (self->handler != mp_const_none) {
|
||||
if (self->ishard) {
|
||||
// When executing code within a handler we must lock the scheduler to
|
||||
// prevent any scheduled callbacks from running, and lock the GC to
|
||||
// prevent any memory allocations.
|
||||
mp_sched_lock();
|
||||
gc_lock();
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_call_function_1(self->handler, self->parent);
|
||||
nlr_pop();
|
||||
} else {
|
||||
// Uncaught exception; disable the callback so that it doesn't run again
|
||||
self->methods->trigger(self->parent, 0);
|
||||
self->handler = mp_const_none;
|
||||
printf("Uncaught exception in IRQ callback handler\n");
|
||||
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
|
||||
}
|
||||
gc_unlock();
|
||||
mp_sched_unlock();
|
||||
} else {
|
||||
// Schedule call to user function
|
||||
mp_sched_schedule(self->handler, self->parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// MicroPython bindings
|
||||
|
||||
STATIC mp_obj_t mp_irq_flags(mp_obj_t self_in) {
|
||||
mp_irq_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_int(self->methods->info(self->parent, MP_IRQ_INFO_FLAGS));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_irq_flags_obj, mp_irq_flags);
|
||||
|
||||
STATIC mp_obj_t mp_irq_trigger(size_t n_args, const mp_obj_t *args) {
|
||||
mp_irq_obj_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
mp_obj_t ret_obj = mp_obj_new_int(self->methods->info(self->parent, MP_IRQ_INFO_TRIGGERS));
|
||||
if (n_args == 2) {
|
||||
// Set trigger
|
||||
self->methods->trigger(self->parent, mp_obj_get_int(args[1]));
|
||||
}
|
||||
return ret_obj;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_irq_trigger_obj, 1, 2, mp_irq_trigger);
|
||||
|
||||
STATIC mp_obj_t mp_irq_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 0, 0, false);
|
||||
mp_irq_handler(MP_OBJ_TO_PTR(self_in));
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_irq_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_flags), MP_ROM_PTR(&mp_irq_flags_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_trigger), MP_ROM_PTR(&mp_irq_trigger_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_irq_locals_dict, mp_irq_locals_dict_table);
|
||||
|
||||
const mp_obj_type_t mp_irq_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_irq,
|
||||
.call = mp_irq_call,
|
||||
.locals_dict = (mp_obj_dict_t *)&mp_irq_locals_dict,
|
||||
};
|
||||
|
||||
#endif // MICROPY_ENABLE_SCHEDULER
|
82
components/language/micropython/shared/runtime/mpirq.h
Normal file
82
components/language/micropython/shared/runtime/mpirq.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Daniel Campora
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H
|
||||
#define MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H
|
||||
|
||||
#include "py/runtime.h"
|
||||
|
||||
/******************************************************************************
|
||||
DEFINE CONSTANTS
|
||||
******************************************************************************/
|
||||
|
||||
enum {
|
||||
MP_IRQ_ARG_INIT_handler = 0,
|
||||
MP_IRQ_ARG_INIT_trigger,
|
||||
MP_IRQ_ARG_INIT_hard,
|
||||
MP_IRQ_ARG_INIT_NUM_ARGS,
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
DEFINE TYPES
|
||||
******************************************************************************/
|
||||
|
||||
typedef mp_uint_t (*mp_irq_trigger_fun_t)(mp_obj_t self, mp_uint_t trigger);
|
||||
typedef mp_uint_t (*mp_irq_info_fun_t)(mp_obj_t self, mp_uint_t info_type);
|
||||
|
||||
enum {
|
||||
MP_IRQ_INFO_FLAGS,
|
||||
MP_IRQ_INFO_TRIGGERS,
|
||||
};
|
||||
|
||||
typedef struct _mp_irq_methods_t {
|
||||
mp_irq_trigger_fun_t trigger;
|
||||
mp_irq_info_fun_t info;
|
||||
} mp_irq_methods_t;
|
||||
|
||||
typedef struct _mp_irq_obj_t {
|
||||
mp_obj_base_t base;
|
||||
mp_irq_methods_t *methods;
|
||||
mp_obj_t parent;
|
||||
mp_obj_t handler;
|
||||
bool ishard;
|
||||
} mp_irq_obj_t;
|
||||
|
||||
/******************************************************************************
|
||||
DECLARE EXPORTED DATA
|
||||
******************************************************************************/
|
||||
|
||||
extern const mp_arg_t mp_irq_init_args[];
|
||||
extern const mp_obj_type_t mp_irq_type;
|
||||
|
||||
/******************************************************************************
|
||||
DECLARE PUBLIC FUNCTIONS
|
||||
******************************************************************************/
|
||||
|
||||
mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent);
|
||||
void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent);
|
||||
void mp_irq_handler(mp_irq_obj_t *self);
|
||||
|
||||
#endif // MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H
|
726
components/language/micropython/shared/runtime/pyexec.c
Normal file
726
components/language/micropython/shared/runtime/pyexec.c
Normal file
@@ -0,0 +1,726 @@
|
||||
/*
|
||||
* This file is part of the MicroPython 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/compile.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/repl.h"
|
||||
#include "py/gc.h"
|
||||
#include "py/frozenmod.h"
|
||||
#include "py/mphal.h"
|
||||
#if MICROPY_HW_ENABLE_USB
|
||||
#include "irq.h"
|
||||
#include "usb.h"
|
||||
#endif
|
||||
#include "shared/readline/readline.h"
|
||||
#include "shared/runtime/pyexec.h"
|
||||
#include "genhdr/mpversion.h"
|
||||
|
||||
pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
|
||||
int pyexec_system_exit = 0;
|
||||
|
||||
#if MICROPY_REPL_INFO
|
||||
STATIC bool repl_display_debugging_info = 0;
|
||||
#endif
|
||||
|
||||
#define EXEC_FLAG_PRINT_EOF (1 << 0)
|
||||
#define EXEC_FLAG_ALLOW_DEBUGGING (1 << 1)
|
||||
#define EXEC_FLAG_IS_REPL (1 << 2)
|
||||
#define EXEC_FLAG_SOURCE_IS_RAW_CODE (1 << 3)
|
||||
#define EXEC_FLAG_SOURCE_IS_VSTR (1 << 4)
|
||||
#define EXEC_FLAG_SOURCE_IS_FILENAME (1 << 5)
|
||||
#define EXEC_FLAG_SOURCE_IS_READER (1 << 6)
|
||||
|
||||
// parses, compiles and executes the code in the lexer
|
||||
// frees the lexer before returning
|
||||
// EXEC_FLAG_PRINT_EOF prints 2 EOF chars: 1 after normal output, 1 after exception output
|
||||
// EXEC_FLAG_ALLOW_DEBUGGING allows debugging info to be printed after executing the code
|
||||
// EXEC_FLAG_IS_REPL is used for REPL inputs (flag passed on to mp_compile)
|
||||
STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input_kind, mp_uint_t exec_flags) {
|
||||
int ret = 0;
|
||||
#if MICROPY_REPL_INFO
|
||||
uint32_t start = 0;
|
||||
#endif
|
||||
|
||||
#ifdef MICROPY_BOARD_BEFORE_PYTHON_EXEC
|
||||
MICROPY_BOARD_BEFORE_PYTHON_EXEC(input_kind, exec_flags);
|
||||
#endif
|
||||
|
||||
// by default a SystemExit exception returns 0
|
||||
pyexec_system_exit = 0;
|
||||
|
||||
nlr_buf_t nlr;
|
||||
nlr.ret_val = NULL;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_obj_t module_fun;
|
||||
#if MICROPY_MODULE_FROZEN_MPY
|
||||
if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) {
|
||||
// source is a raw_code object, create the function
|
||||
const mp_frozen_module_t *frozen = source;
|
||||
mp_module_context_t *ctx = m_new_obj(mp_module_context_t);
|
||||
ctx->module.globals = mp_globals_get();
|
||||
ctx->constants = frozen->constants;
|
||||
module_fun = mp_make_function_from_raw_code(frozen->rc, ctx, NULL);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#if MICROPY_ENABLE_COMPILER
|
||||
mp_lexer_t *lex;
|
||||
if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) {
|
||||
const vstr_t *vstr = source;
|
||||
lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0);
|
||||
} else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) {
|
||||
lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source);
|
||||
} else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) {
|
||||
lex = mp_lexer_new_from_file(source);
|
||||
} else {
|
||||
lex = (mp_lexer_t *)source;
|
||||
}
|
||||
// source is a lexer, parse and compile the script
|
||||
qstr source_name = lex->source_name;
|
||||
mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
|
||||
module_fun = mp_compile(&parse_tree, source_name, exec_flags & EXEC_FLAG_IS_REPL);
|
||||
#else
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("script compilation not supported"));
|
||||
#endif
|
||||
}
|
||||
|
||||
// execute code
|
||||
mp_hal_set_interrupt_char(CHAR_CTRL_C); // allow ctrl-C to interrupt us
|
||||
#if MICROPY_REPL_INFO
|
||||
start = mp_hal_ticks_ms();
|
||||
#endif
|
||||
mp_call_function_0(module_fun);
|
||||
mp_hal_set_interrupt_char(-1); // disable interrupt
|
||||
mp_handle_pending(true); // handle any pending exceptions (and any callbacks)
|
||||
nlr_pop();
|
||||
ret = 1;
|
||||
if (exec_flags & EXEC_FLAG_PRINT_EOF) {
|
||||
mp_hal_stdout_tx_strn("\x04", 1);
|
||||
}
|
||||
} else {
|
||||
// uncaught exception
|
||||
mp_hal_set_interrupt_char(-1); // disable interrupt
|
||||
mp_handle_pending(false); // clear any pending exceptions (and run any callbacks)
|
||||
|
||||
if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) {
|
||||
const mp_reader_t *reader = source;
|
||||
reader->close(reader->data);
|
||||
}
|
||||
|
||||
// print EOF after normal output
|
||||
if (exec_flags & EXEC_FLAG_PRINT_EOF) {
|
||||
mp_hal_stdout_tx_strn("\x04", 1);
|
||||
}
|
||||
|
||||
// check for SystemExit
|
||||
if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
|
||||
// at the moment, the value of SystemExit is unused
|
||||
ret = pyexec_system_exit;
|
||||
} else {
|
||||
mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#if MICROPY_REPL_INFO
|
||||
// display debugging info if wanted
|
||||
if ((exec_flags & EXEC_FLAG_ALLOW_DEBUGGING) && repl_display_debugging_info) {
|
||||
mp_uint_t ticks = mp_hal_ticks_ms() - start; // TODO implement a function that does this properly
|
||||
printf("took " UINT_FMT " ms\n", ticks);
|
||||
// qstr info
|
||||
{
|
||||
size_t 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",
|
||||
(unsigned)n_pool, (unsigned)n_qstr, (unsigned)n_str_data_bytes, (unsigned)n_total_bytes);
|
||||
}
|
||||
|
||||
#if MICROPY_ENABLE_GC
|
||||
// run collection and print GC info
|
||||
gc_collect();
|
||||
gc_dump_info();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if (exec_flags & EXEC_FLAG_PRINT_EOF) {
|
||||
mp_hal_stdout_tx_strn("\x04", 1);
|
||||
}
|
||||
|
||||
#ifdef MICROPY_BOARD_AFTER_PYTHON_EXEC
|
||||
MICROPY_BOARD_AFTER_PYTHON_EXEC(input_kind, exec_flags, nlr.ret_val, &ret);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if MICROPY_ENABLE_COMPILER
|
||||
|
||||
// This can be configured by a port (and even configured to a function to be
|
||||
// computed dynamically) to indicate the maximum number of bytes that can be
|
||||
// held in the stdin buffer.
|
||||
#ifndef MICROPY_REPL_STDIN_BUFFER_MAX
|
||||
#define MICROPY_REPL_STDIN_BUFFER_MAX (256)
|
||||
#endif
|
||||
|
||||
typedef struct _mp_reader_stdin_t {
|
||||
bool eof;
|
||||
uint16_t window_max;
|
||||
uint16_t window_remain;
|
||||
} mp_reader_stdin_t;
|
||||
|
||||
STATIC mp_uint_t mp_reader_stdin_readbyte(void *data) {
|
||||
mp_reader_stdin_t *reader = (mp_reader_stdin_t *)data;
|
||||
|
||||
if (reader->eof) {
|
||||
return MP_READER_EOF;
|
||||
}
|
||||
|
||||
int c = mp_hal_stdin_rx_chr();
|
||||
|
||||
if (c == CHAR_CTRL_C || c == CHAR_CTRL_D) {
|
||||
reader->eof = true;
|
||||
mp_hal_stdout_tx_strn("\x04", 1); // indicate end to host
|
||||
if (c == CHAR_CTRL_C) {
|
||||
#if MICROPY_KBD_EXCEPTION
|
||||
MP_STATE_VM(mp_kbd_exception).traceback_data = NULL;
|
||||
nlr_raise(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)));
|
||||
#else
|
||||
mp_raise_type(&mp_type_KeyboardInterrupt);
|
||||
#endif
|
||||
} else {
|
||||
return MP_READER_EOF;
|
||||
}
|
||||
}
|
||||
|
||||
if (--reader->window_remain == 0) {
|
||||
mp_hal_stdout_tx_strn("\x01", 1); // indicate window available to host
|
||||
reader->window_remain = reader->window_max;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
STATIC void mp_reader_stdin_close(void *data) {
|
||||
mp_reader_stdin_t *reader = (mp_reader_stdin_t *)data;
|
||||
if (!reader->eof) {
|
||||
reader->eof = true;
|
||||
mp_hal_stdout_tx_strn("\x04", 1); // indicate end to host
|
||||
for (;;) {
|
||||
int c = mp_hal_stdin_rx_chr();
|
||||
if (c == CHAR_CTRL_C || c == CHAR_CTRL_D) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void mp_reader_new_stdin(mp_reader_t *reader, mp_reader_stdin_t *reader_stdin, uint16_t buf_max) {
|
||||
// Make flow-control window half the buffer size, and indicate to the host that 2x windows are
|
||||
// free (sending the window size implicitly indicates that a window is free, and then the 0x01
|
||||
// indicates that another window is free).
|
||||
size_t window = buf_max / 2;
|
||||
char reply[3] = { window & 0xff, window >> 8, 0x01 };
|
||||
mp_hal_stdout_tx_strn(reply, sizeof(reply));
|
||||
|
||||
reader_stdin->eof = false;
|
||||
reader_stdin->window_max = window;
|
||||
reader_stdin->window_remain = window;
|
||||
reader->data = reader_stdin;
|
||||
reader->readbyte = mp_reader_stdin_readbyte;
|
||||
reader->close = mp_reader_stdin_close;
|
||||
}
|
||||
|
||||
STATIC int do_reader_stdin(int c) {
|
||||
if (c != 'A') {
|
||||
// Unsupported command.
|
||||
mp_hal_stdout_tx_strn("R\x00", 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Indicate reception of command.
|
||||
mp_hal_stdout_tx_strn("R\x01", 2);
|
||||
|
||||
mp_reader_t reader;
|
||||
mp_reader_stdin_t reader_stdin;
|
||||
mp_reader_new_stdin(&reader, &reader_stdin, MICROPY_REPL_STDIN_BUFFER_MAX);
|
||||
int exec_flags = EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_READER;
|
||||
return parse_compile_execute(&reader, MP_PARSE_FILE_INPUT, exec_flags);
|
||||
}
|
||||
|
||||
#if MICROPY_REPL_EVENT_DRIVEN
|
||||
|
||||
typedef struct _repl_t {
|
||||
// This structure originally also held current REPL line,
|
||||
// but it was moved to MP_STATE_VM(repl_line) as containing
|
||||
// root pointer. Still keep structure in case more state
|
||||
// will be added later.
|
||||
// vstr_t line;
|
||||
bool cont_line;
|
||||
bool paste_mode;
|
||||
} repl_t;
|
||||
|
||||
repl_t repl;
|
||||
|
||||
STATIC int pyexec_raw_repl_process_char(int c);
|
||||
STATIC int pyexec_friendly_repl_process_char(int c);
|
||||
|
||||
void pyexec_event_repl_init(void) {
|
||||
MP_STATE_VM(repl_line) = vstr_new(32);
|
||||
repl.cont_line = false;
|
||||
repl.paste_mode = false;
|
||||
// no prompt before printing friendly REPL banner or entering raw REPL
|
||||
readline_init(MP_STATE_VM(repl_line), "");
|
||||
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
|
||||
pyexec_raw_repl_process_char(CHAR_CTRL_A);
|
||||
} else {
|
||||
pyexec_friendly_repl_process_char(CHAR_CTRL_B);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC int pyexec_raw_repl_process_char(int c) {
|
||||
if (c == CHAR_CTRL_A) {
|
||||
// reset raw REPL
|
||||
if (vstr_len(MP_STATE_VM(repl_line)) == 2 && vstr_str(MP_STATE_VM(repl_line))[0] == CHAR_CTRL_E) {
|
||||
int ret = do_reader_stdin(vstr_str(MP_STATE_VM(repl_line))[1]);
|
||||
if (ret & PYEXEC_FORCED_EXIT) {
|
||||
return ret;
|
||||
}
|
||||
goto reset;
|
||||
}
|
||||
mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n");
|
||||
goto reset;
|
||||
} else if (c == CHAR_CTRL_B) {
|
||||
// change to friendly REPL
|
||||
pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
|
||||
vstr_reset(MP_STATE_VM(repl_line));
|
||||
repl.cont_line = false;
|
||||
repl.paste_mode = false;
|
||||
pyexec_friendly_repl_process_char(CHAR_CTRL_B);
|
||||
return 0;
|
||||
} else if (c == CHAR_CTRL_C) {
|
||||
// clear line
|
||||
vstr_reset(MP_STATE_VM(repl_line));
|
||||
return 0;
|
||||
} else if (c == CHAR_CTRL_D) {
|
||||
// input finished
|
||||
} else {
|
||||
// let through any other raw 8-bit value
|
||||
vstr_add_byte(MP_STATE_VM(repl_line), c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// indicate reception of command
|
||||
mp_hal_stdout_tx_str("OK");
|
||||
|
||||
if (MP_STATE_VM(repl_line)->len == 0) {
|
||||
// exit for a soft reset
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
vstr_clear(MP_STATE_VM(repl_line));
|
||||
return PYEXEC_FORCED_EXIT;
|
||||
}
|
||||
|
||||
int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR);
|
||||
if (ret & PYEXEC_FORCED_EXIT) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
reset:
|
||||
vstr_reset(MP_STATE_VM(repl_line));
|
||||
mp_hal_stdout_tx_str(">");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC int pyexec_friendly_repl_process_char(int c) {
|
||||
if (repl.paste_mode) {
|
||||
if (c == CHAR_CTRL_C) {
|
||||
// cancel everything
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
goto input_restart;
|
||||
} else if (c == CHAR_CTRL_D) {
|
||||
// end of input
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_FILE_INPUT, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR);
|
||||
if (ret & PYEXEC_FORCED_EXIT) {
|
||||
return ret;
|
||||
}
|
||||
goto input_restart;
|
||||
} else {
|
||||
// add char to buffer and echo
|
||||
vstr_add_byte(MP_STATE_VM(repl_line), c);
|
||||
if (c == '\r') {
|
||||
mp_hal_stdout_tx_str("\r\n=== ");
|
||||
} else {
|
||||
char buf[1] = {c};
|
||||
mp_hal_stdout_tx_strn(buf, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int ret = readline_process_char(c);
|
||||
|
||||
if (!repl.cont_line) {
|
||||
|
||||
if (ret == CHAR_CTRL_A) {
|
||||
// change to raw REPL
|
||||
pyexec_mode_kind = PYEXEC_MODE_RAW_REPL;
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
pyexec_raw_repl_process_char(CHAR_CTRL_A);
|
||||
return 0;
|
||||
} else if (ret == CHAR_CTRL_B) {
|
||||
// reset friendly REPL
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
mp_hal_stdout_tx_str(MICROPY_BANNER_NAME_AND_VERSION);
|
||||
mp_hal_stdout_tx_str("; " MICROPY_BANNER_MACHINE);
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
#if MICROPY_PY_BUILTINS_HELP
|
||||
mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n");
|
||||
#endif
|
||||
goto input_restart;
|
||||
} else if (ret == CHAR_CTRL_C) {
|
||||
// break
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
goto input_restart;
|
||||
} else if (ret == CHAR_CTRL_D) {
|
||||
// exit for a soft reset
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
vstr_clear(MP_STATE_VM(repl_line));
|
||||
return PYEXEC_FORCED_EXIT;
|
||||
} else if (ret == CHAR_CTRL_E) {
|
||||
// paste mode
|
||||
mp_hal_stdout_tx_str("\r\npaste mode; Ctrl-C to cancel, Ctrl-D to finish\r\n=== ");
|
||||
vstr_reset(MP_STATE_VM(repl_line));
|
||||
repl.paste_mode = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!mp_repl_continue_with_input(vstr_null_terminated_str(MP_STATE_VM(repl_line)))) {
|
||||
goto exec;
|
||||
}
|
||||
|
||||
vstr_add_byte(MP_STATE_VM(repl_line), '\n');
|
||||
repl.cont_line = true;
|
||||
readline_note_newline(mp_repl_get_ps2());
|
||||
return 0;
|
||||
|
||||
} else {
|
||||
|
||||
if (ret == CHAR_CTRL_C) {
|
||||
// cancel everything
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
repl.cont_line = false;
|
||||
goto input_restart;
|
||||
} else if (ret == CHAR_CTRL_D) {
|
||||
// stop entering compound statement
|
||||
goto exec;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mp_repl_continue_with_input(vstr_null_terminated_str(MP_STATE_VM(repl_line)))) {
|
||||
vstr_add_byte(MP_STATE_VM(repl_line), '\n');
|
||||
readline_note_newline(mp_repl_get_ps2());
|
||||
return 0;
|
||||
}
|
||||
|
||||
exec:;
|
||||
int ret = parse_compile_execute(MP_STATE_VM(repl_line), MP_PARSE_SINGLE_INPUT, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR);
|
||||
if (ret & PYEXEC_FORCED_EXIT) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
input_restart:
|
||||
vstr_reset(MP_STATE_VM(repl_line));
|
||||
repl.cont_line = false;
|
||||
repl.paste_mode = false;
|
||||
readline_init(MP_STATE_VM(repl_line), mp_repl_get_ps1());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t pyexec_repl_active;
|
||||
int pyexec_event_repl_process_char(int c) {
|
||||
pyexec_repl_active = 1;
|
||||
int res;
|
||||
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
|
||||
res = pyexec_raw_repl_process_char(c);
|
||||
} else {
|
||||
res = pyexec_friendly_repl_process_char(c);
|
||||
}
|
||||
pyexec_repl_active = 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
#else // MICROPY_REPL_EVENT_DRIVEN
|
||||
|
||||
int pyexec_raw_repl(void) {
|
||||
vstr_t line;
|
||||
vstr_init(&line, 32);
|
||||
|
||||
raw_repl_reset:
|
||||
mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n");
|
||||
|
||||
for (;;) {
|
||||
vstr_reset(&line);
|
||||
mp_hal_stdout_tx_str(">");
|
||||
for (;;) {
|
||||
int c = mp_hal_stdin_rx_chr();
|
||||
if (c == CHAR_CTRL_A) {
|
||||
// reset raw REPL
|
||||
if (vstr_len(&line) == 2 && vstr_str(&line)[0] == CHAR_CTRL_E) {
|
||||
int ret = do_reader_stdin(vstr_str(&line)[1]);
|
||||
if (ret & PYEXEC_FORCED_EXIT) {
|
||||
return ret;
|
||||
}
|
||||
vstr_reset(&line);
|
||||
mp_hal_stdout_tx_str(">");
|
||||
continue;
|
||||
}
|
||||
goto raw_repl_reset;
|
||||
} else if (c == CHAR_CTRL_B) {
|
||||
// change to friendly REPL
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
vstr_clear(&line);
|
||||
pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
|
||||
return 0;
|
||||
} else if (c == CHAR_CTRL_C) {
|
||||
// clear line
|
||||
vstr_reset(&line);
|
||||
} else if (c == CHAR_CTRL_D) {
|
||||
// input finished
|
||||
break;
|
||||
} else {
|
||||
// let through any other raw 8-bit value
|
||||
vstr_add_byte(&line, c);
|
||||
}
|
||||
}
|
||||
|
||||
// indicate reception of command
|
||||
mp_hal_stdout_tx_str("OK");
|
||||
|
||||
if (line.len == 0) {
|
||||
// exit for a soft reset
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
vstr_clear(&line);
|
||||
return PYEXEC_FORCED_EXIT;
|
||||
}
|
||||
|
||||
int ret = parse_compile_execute(&line, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR);
|
||||
if (ret & PYEXEC_FORCED_EXIT) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int pyexec_friendly_repl(void) {
|
||||
vstr_t line;
|
||||
vstr_init(&line, 32);
|
||||
|
||||
friendly_repl_reset:
|
||||
mp_hal_stdout_tx_str(MICROPY_BANNER_NAME_AND_VERSION);
|
||||
mp_hal_stdout_tx_str("; " MICROPY_BANNER_MACHINE);
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
#if MICROPY_PY_BUILTINS_HELP
|
||||
mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n");
|
||||
#endif
|
||||
|
||||
// to test ctrl-C
|
||||
/*
|
||||
{
|
||||
uint32_t x[4] = {0x424242, 0xdeaddead, 0x242424, 0xdeadbeef};
|
||||
for (;;) {
|
||||
nlr_buf_t nlr;
|
||||
printf("pyexec_repl: %p\n", x);
|
||||
mp_hal_set_interrupt_char(CHAR_CTRL_C);
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
for (;;) {
|
||||
}
|
||||
} else {
|
||||
printf("break\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
for (;;) {
|
||||
input_restart:
|
||||
|
||||
#if MICROPY_HW_ENABLE_USB
|
||||
if (usb_vcp_is_enabled()) {
|
||||
// If the user gets to here and interrupts are disabled then
|
||||
// they'll never see the prompt, traceback etc. The USB REPL needs
|
||||
// interrupts to be enabled or no transfers occur. So we try to
|
||||
// do the user a favor and reenable interrupts.
|
||||
if (query_irq() == IRQ_STATE_DISABLED) {
|
||||
enable_irq(IRQ_STATE_ENABLED);
|
||||
mp_hal_stdout_tx_str("MPY: enabling IRQs\r\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the GC is locked at this point there is no way out except a reset,
|
||||
// so force the GC to be unlocked to help the user debug what went wrong.
|
||||
if (MP_STATE_THREAD(gc_lock_depth) != 0) {
|
||||
MP_STATE_THREAD(gc_lock_depth) = 0;
|
||||
}
|
||||
|
||||
vstr_reset(&line);
|
||||
int ret = readline(&line, mp_repl_get_ps1());
|
||||
mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT;
|
||||
|
||||
if (ret == CHAR_CTRL_A) {
|
||||
// change to raw REPL
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
vstr_clear(&line);
|
||||
pyexec_mode_kind = PYEXEC_MODE_RAW_REPL;
|
||||
return 0;
|
||||
} else if (ret == CHAR_CTRL_B) {
|
||||
// reset friendly REPL
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
goto friendly_repl_reset;
|
||||
} else if (ret == CHAR_CTRL_C) {
|
||||
// break
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
continue;
|
||||
} else if (ret == CHAR_CTRL_D) {
|
||||
// exit for a soft reset
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
vstr_clear(&line);
|
||||
return PYEXEC_FORCED_EXIT;
|
||||
} else if (ret == CHAR_CTRL_E) {
|
||||
// paste mode
|
||||
mp_hal_stdout_tx_str("\r\npaste mode; Ctrl-C to cancel, Ctrl-D to finish\r\n=== ");
|
||||
vstr_reset(&line);
|
||||
for (;;) {
|
||||
char c = mp_hal_stdin_rx_chr();
|
||||
if (c == CHAR_CTRL_C) {
|
||||
// cancel everything
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
goto input_restart;
|
||||
} else if (c == CHAR_CTRL_D) {
|
||||
// end of input
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
break;
|
||||
} else {
|
||||
// add char to buffer and echo
|
||||
vstr_add_byte(&line, c);
|
||||
if (c == '\r') {
|
||||
mp_hal_stdout_tx_str("\r\n=== ");
|
||||
} else {
|
||||
mp_hal_stdout_tx_strn(&c, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
parse_input_kind = MP_PARSE_FILE_INPUT;
|
||||
} else if (vstr_len(&line) == 0) {
|
||||
continue;
|
||||
} else {
|
||||
// got a line with non-zero length, see if it needs continuing
|
||||
while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) {
|
||||
vstr_add_byte(&line, '\n');
|
||||
ret = readline(&line, mp_repl_get_ps2());
|
||||
if (ret == CHAR_CTRL_C) {
|
||||
// cancel everything
|
||||
mp_hal_stdout_tx_str("\r\n");
|
||||
goto input_restart;
|
||||
} else if (ret == CHAR_CTRL_D) {
|
||||
// stop entering compound statement
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = parse_compile_execute(&line, parse_input_kind, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR);
|
||||
if (ret & PYEXEC_FORCED_EXIT) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MICROPY_REPL_EVENT_DRIVEN
|
||||
#endif // MICROPY_ENABLE_COMPILER
|
||||
|
||||
int pyexec_file(const char *filename) {
|
||||
return parse_compile_execute(filename, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_FILENAME);
|
||||
}
|
||||
|
||||
int pyexec_file_if_exists(const char *filename) {
|
||||
#if MICROPY_MODULE_FROZEN
|
||||
if (mp_find_frozen_module(filename, NULL, NULL) == MP_IMPORT_STAT_FILE) {
|
||||
return pyexec_frozen_module(filename);
|
||||
}
|
||||
#endif
|
||||
if (mp_import_stat(filename) != MP_IMPORT_STAT_FILE) {
|
||||
return 1; // success (no file is the same as an empty file executing without fail)
|
||||
}
|
||||
return pyexec_file(filename);
|
||||
}
|
||||
|
||||
#if MICROPY_MODULE_FROZEN
|
||||
int pyexec_frozen_module(const char *name) {
|
||||
void *frozen_data;
|
||||
int frozen_type;
|
||||
mp_find_frozen_module(name, &frozen_type, &frozen_data);
|
||||
|
||||
switch (frozen_type) {
|
||||
#if MICROPY_MODULE_FROZEN_STR
|
||||
case MP_FROZEN_STR:
|
||||
return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, 0);
|
||||
#endif
|
||||
|
||||
#if MICROPY_MODULE_FROZEN_MPY
|
||||
case MP_FROZEN_MPY:
|
||||
return parse_compile_execute(frozen_data, MP_PARSE_FILE_INPUT, EXEC_FLAG_SOURCE_IS_RAW_CODE);
|
||||
#endif
|
||||
|
||||
default:
|
||||
printf("could not find module '%s'\n", name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MICROPY_REPL_INFO
|
||||
mp_obj_t pyb_set_repl_info(mp_obj_t o_value) {
|
||||
repl_display_debugging_info = mp_obj_get_int(o_value);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(pyb_set_repl_info_obj, pyb_set_repl_info);
|
||||
#endif
|
59
components/language/micropython/shared/runtime/pyexec.h
Normal file
59
components/language/micropython/shared/runtime/pyexec.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* This file is part of the MicroPython 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.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H
|
||||
#define MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H
|
||||
|
||||
#include "py/obj.h"
|
||||
|
||||
typedef enum {
|
||||
PYEXEC_MODE_FRIENDLY_REPL,
|
||||
PYEXEC_MODE_RAW_REPL,
|
||||
} pyexec_mode_kind_t;
|
||||
|
||||
extern pyexec_mode_kind_t pyexec_mode_kind;
|
||||
|
||||
// Set this to the value (eg PYEXEC_FORCED_EXIT) that will be propagated through
|
||||
// the pyexec functions if a SystemExit exception is raised by the running code.
|
||||
// It will reset to 0 at the start of each execution (eg each REPL entry).
|
||||
extern int pyexec_system_exit;
|
||||
|
||||
#define PYEXEC_FORCED_EXIT (0x100)
|
||||
|
||||
int pyexec_raw_repl(void);
|
||||
int pyexec_friendly_repl(void);
|
||||
int pyexec_file(const char *filename);
|
||||
int pyexec_file_if_exists(const char *filename);
|
||||
int pyexec_frozen_module(const char *name);
|
||||
void pyexec_event_repl_init(void);
|
||||
int pyexec_event_repl_process_char(int c);
|
||||
extern uint8_t pyexec_repl_active;
|
||||
|
||||
#if MICROPY_REPL_INFO
|
||||
mp_obj_t pyb_set_repl_info(mp_obj_t o_value);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(pyb_set_repl_info_obj);
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_LIB_UTILS_PYEXEC_H
|
132
components/language/micropython/shared/runtime/semihosting.c
Normal file
132
components/language/micropython/shared/runtime/semihosting.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Ayke van Laethem
|
||||
*
|
||||
* 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 "semihosting.h"
|
||||
|
||||
// Resources:
|
||||
// http://embed.rs/articles/2016/semi-hosting-rust/
|
||||
// https://wiki.dlang.org/Minimal_semihosted_ARM_Cortex-M_%22Hello_World%22
|
||||
// https://github.com/arduino/OpenOCD/blob/master/src/target/arm_semihosting.c
|
||||
|
||||
#define SYS_OPEN 0x01
|
||||
#define SYS_WRITEC 0x03
|
||||
#define SYS_WRITE 0x05
|
||||
#define SYS_READC 0x07
|
||||
|
||||
// Constants:
|
||||
#define OPEN_MODE_READ (0) // mode "r"
|
||||
#define OPEN_MODE_WRITE (4) // mode "w"
|
||||
|
||||
#ifndef __thumb__
|
||||
#error Semihosting is only implemented for ARM microcontrollers.
|
||||
#endif
|
||||
|
||||
static int mp_semihosting_stdout;
|
||||
|
||||
static uint32_t mp_semihosting_call(uint32_t num, const void *arg) {
|
||||
// A semihosting call works as follows, similar to a SVCall:
|
||||
// * the call is invoked by a special breakpoint: 0xAB
|
||||
// * the command is placed in r0
|
||||
// * a pointer to the arguments is placed in r1
|
||||
// * the return value is placed in r0
|
||||
// Note that because it uses the breakpoint instruction, applications
|
||||
// will hang if they're not connected to a debugger. And they'll be
|
||||
// stuck in a breakpoint if semihosting is not specifically enabled in
|
||||
// the debugger.
|
||||
// Also note that semihosting is extremely slow (sometimes >100ms per
|
||||
// call).
|
||||
register uint32_t num_reg __asm__ ("r0") = num;
|
||||
register const void *args_reg __asm__ ("r1") = arg;
|
||||
__asm__ __volatile__ (
|
||||
"bkpt 0xAB\n" // invoke semihosting call
|
||||
: "+r" (num_reg) // call number and result
|
||||
: "r" (args_reg) // arguments
|
||||
: "memory"); // make sure args aren't optimized away
|
||||
return num_reg; // r0, which became the result
|
||||
}
|
||||
|
||||
static int mp_semihosting_open_console(uint32_t mode) {
|
||||
struct {
|
||||
char *name;
|
||||
uint32_t mode;
|
||||
uint32_t name_len;
|
||||
} args = {
|
||||
.name = ":tt", // magic path to console
|
||||
.mode = mode, // e.g. "r", "w" (see OPEN_MODE_* constants)
|
||||
.name_len = 3, // strlen(":tt")
|
||||
};
|
||||
return mp_semihosting_call(SYS_OPEN, &args);
|
||||
}
|
||||
|
||||
void mp_semihosting_init() {
|
||||
mp_semihosting_stdout = mp_semihosting_open_console(OPEN_MODE_WRITE);
|
||||
}
|
||||
|
||||
int mp_semihosting_rx_char() {
|
||||
return mp_semihosting_call(SYS_READC, NULL);
|
||||
}
|
||||
|
||||
static void mp_semihosting_tx_char(char c) {
|
||||
mp_semihosting_call(SYS_WRITEC, &c);
|
||||
}
|
||||
|
||||
uint32_t mp_semihosting_tx_strn(const char *str, size_t len) {
|
||||
if (len == 0) {
|
||||
return 0; // nothing to do
|
||||
}
|
||||
if (len == 1) {
|
||||
mp_semihosting_tx_char(*str); // maybe faster?
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct {
|
||||
uint32_t fd;
|
||||
const char *str;
|
||||
uint32_t len;
|
||||
} args = {
|
||||
.fd = mp_semihosting_stdout,
|
||||
.str = str,
|
||||
.len = len,
|
||||
};
|
||||
return mp_semihosting_call(SYS_WRITE, &args);
|
||||
}
|
||||
|
||||
uint32_t mp_semihosting_tx_strn_cooked(const char *str, size_t len) {
|
||||
// Write chunks of data until (excluding) the first '\n' character,
|
||||
// insert a '\r' character, and then continue with the next chunk
|
||||
// (starting with '\n').
|
||||
// Doing byte-by-byte writes would be easier to implement but is far
|
||||
// too slow.
|
||||
size_t start = 0;
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (str[i] == '\n') {
|
||||
mp_semihosting_tx_strn(str + start, i - start);
|
||||
mp_semihosting_tx_char('\r');
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
return mp_semihosting_tx_strn(str + start, len - start);
|
||||
}
|
51
components/language/micropython/shared/runtime/semihosting.h
Normal file
51
components/language/micropython/shared/runtime/semihosting.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Ayke van Laethem
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_LIB_UTILS_SEMIHOSTING_H
|
||||
#define MICROPY_INCLUDED_LIB_UTILS_SEMIHOSTING_H
|
||||
|
||||
/*
|
||||
|
||||
To use semi-hosting for a replacement UART:
|
||||
- Add lib/semihosting/semihosting.c to the Makefile sources.
|
||||
- Call mp_semihosting_init() in main(), around the time UART is initialized.
|
||||
- Replace mp_hal_stdin_rx_chr and similar in mphalport.c with the semihosting equivalent.
|
||||
- Include lib/semihosting/semihosting.h in the relevant files.
|
||||
|
||||
Then make sure the debugger is attached and enables semihosting. In OpenOCD this is
|
||||
done with ARM semihosting enable followed by reset. The terminal will need further
|
||||
configuration to work with MicroPython (bash: stty raw -echo).
|
||||
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void mp_semihosting_init();
|
||||
int mp_semihosting_rx_char();
|
||||
uint32_t mp_semihosting_tx_strn(const char *str, size_t len);
|
||||
uint32_t mp_semihosting_tx_strn_cooked(const char *str, size_t len);
|
||||
|
||||
#endif // MICROPY_INCLUDED_LIB_UTILS_SEMIHOSTING_H
|
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2021 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 <string.h>
|
||||
#include "py/mphal.h"
|
||||
|
||||
/*
|
||||
* Extra stdout functions
|
||||
* These can be either optimized for a particular port, or reference
|
||||
* implementation below can be used.
|
||||
*/
|
||||
|
||||
// Send "cooked" string of given length, where every occurrence of
|
||||
// LF character is replaced with CR LF ("\n" is converted to "\r\n").
|
||||
// This is an optimised version to reduce the number of calls made
|
||||
// to mp_hal_stdout_tx_strn.
|
||||
void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) {
|
||||
const char *last = str;
|
||||
while (len--) {
|
||||
if (*str == '\n') {
|
||||
if (str > last) {
|
||||
mp_hal_stdout_tx_strn(last, str - last);
|
||||
}
|
||||
mp_hal_stdout_tx_strn("\r\n", 2);
|
||||
++str;
|
||||
last = str;
|
||||
} else {
|
||||
++str;
|
||||
}
|
||||
}
|
||||
if (str > last) {
|
||||
mp_hal_stdout_tx_strn(last, str - last);
|
||||
}
|
||||
}
|
||||
|
||||
// Send zero-terminated string
|
||||
void mp_hal_stdout_tx_str(const char *str) {
|
||||
mp_hal_stdout_tx_strn(str, strlen(str));
|
||||
}
|
175
components/language/micropython/shared/runtime/sys_stdio_mphal.c
Normal file
175
components/language/micropython/shared/runtime/sys_stdio_mphal.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2019 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 "py/obj.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/mperrno.h"
|
||||
#include "py/mphal.h"
|
||||
|
||||
// TODO make stdin, stdout and stderr writable objects so they can
|
||||
// be changed by Python code. This requires some changes, as these
|
||||
// objects are in a read-only module (py/modsys.c).
|
||||
|
||||
/******************************************************************************/
|
||||
// MicroPython bindings
|
||||
|
||||
#define STDIO_FD_IN (0)
|
||||
#define STDIO_FD_OUT (1)
|
||||
#define STDIO_FD_ERR (2)
|
||||
|
||||
typedef struct _sys_stdio_obj_t {
|
||||
mp_obj_base_t base;
|
||||
int fd;
|
||||
} sys_stdio_obj_t;
|
||||
|
||||
#if MICROPY_PY_SYS_STDIO_BUFFER
|
||||
STATIC const sys_stdio_obj_t stdio_buffer_obj;
|
||||
#endif
|
||||
|
||||
void stdio_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
sys_stdio_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_printf(print, "<io.FileIO %d>", self->fd);
|
||||
}
|
||||
|
||||
STATIC mp_uint_t stdio_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
|
||||
sys_stdio_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->fd == STDIO_FD_IN) {
|
||||
for (uint i = 0; i < size; i++) {
|
||||
int c = mp_hal_stdin_rx_chr();
|
||||
if (c == '\r') {
|
||||
c = '\n';
|
||||
}
|
||||
((byte *)buf)[i] = c;
|
||||
}
|
||||
return size;
|
||||
} else {
|
||||
*errcode = MP_EPERM;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_uint_t stdio_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
|
||||
sys_stdio_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->fd == STDIO_FD_OUT || self->fd == STDIO_FD_ERR) {
|
||||
mp_hal_stdout_tx_strn_cooked(buf, size);
|
||||
return size;
|
||||
} else {
|
||||
*errcode = MP_EPERM;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_uint_t stdio_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) {
|
||||
(void)self_in;
|
||||
if (request == MP_STREAM_POLL) {
|
||||
return mp_hal_stdio_poll(arg);
|
||||
} else {
|
||||
*errcode = MP_EINVAL;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t stdio_obj___exit__(size_t n_args, const mp_obj_t *args) {
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(stdio_obj___exit___obj, 4, 4, stdio_obj___exit__);
|
||||
|
||||
// TODO gc hook to close the file if not already closed
|
||||
|
||||
STATIC const mp_rom_map_elem_t stdio_locals_dict_table[] = {
|
||||
#if MICROPY_PY_SYS_STDIO_BUFFER
|
||||
{ MP_ROM_QSTR(MP_QSTR_buffer), MP_ROM_PTR(&stdio_buffer_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_readlines), MP_ROM_PTR(&mp_stream_unbuffered_readlines_obj)},
|
||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_identity_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_identity_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&stdio_obj___exit___obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(stdio_locals_dict, stdio_locals_dict_table);
|
||||
|
||||
STATIC const mp_stream_p_t stdio_obj_stream_p = {
|
||||
.read = stdio_read,
|
||||
.write = stdio_write,
|
||||
.ioctl = stdio_ioctl,
|
||||
.is_text = true,
|
||||
};
|
||||
|
||||
STATIC const mp_obj_type_t stdio_obj_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_FileIO,
|
||||
// TODO .make_new?
|
||||
.print = stdio_obj_print,
|
||||
.getiter = mp_identity_getiter,
|
||||
.iternext = mp_stream_unbuffered_iter,
|
||||
.protocol = &stdio_obj_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&stdio_locals_dict,
|
||||
};
|
||||
|
||||
const sys_stdio_obj_t mp_sys_stdin_obj = {{&stdio_obj_type}, .fd = STDIO_FD_IN};
|
||||
const sys_stdio_obj_t mp_sys_stdout_obj = {{&stdio_obj_type}, .fd = STDIO_FD_OUT};
|
||||
const sys_stdio_obj_t mp_sys_stderr_obj = {{&stdio_obj_type}, .fd = STDIO_FD_ERR};
|
||||
|
||||
#if MICROPY_PY_SYS_STDIO_BUFFER
|
||||
STATIC mp_uint_t stdio_buffer_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) {
|
||||
for (uint i = 0; i < size; i++) {
|
||||
((byte *)buf)[i] = mp_hal_stdin_rx_chr();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t stdio_buffer_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
|
||||
mp_hal_stdout_tx_strn(buf, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
STATIC const mp_stream_p_t stdio_buffer_obj_stream_p = {
|
||||
.read = stdio_buffer_read,
|
||||
.write = stdio_buffer_write,
|
||||
.ioctl = stdio_ioctl,
|
||||
.is_text = false,
|
||||
};
|
||||
|
||||
STATIC const mp_obj_type_t stdio_buffer_obj_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_FileIO,
|
||||
.print = stdio_obj_print,
|
||||
.getiter = mp_identity_getiter,
|
||||
.iternext = mp_stream_unbuffered_iter,
|
||||
.protocol = &stdio_buffer_obj_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&stdio_locals_dict,
|
||||
};
|
||||
|
||||
STATIC const sys_stdio_obj_t stdio_buffer_obj = {{&stdio_buffer_obj_type}, .fd = 0}; // fd unused
|
||||
#endif
|
Reference in New Issue
Block a user