8000 emscripten: Add support for importing modules · matthewelse/micropython@dacdc59 · GitHub
[go: up one dir, main page]

Skip to content

Commit dacdc59

Browse files
committed
emscripten: Add support for importing modules
1 parent 00dbe31 commit dacdc59

File tree

2 files changed

+99
-161
lines changed

2 files changed

+99
-161
lines changed

emscripten/main.c

Lines changed: 97 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include <stdint.h>
22
#include <stdio.h>
3+
#include <stdlib.h>
34
#include <string.h>
5+
#include <sys/stat.h>
46

57
#include "py/nlr.h"
68
#include "py/compile.h"
@@ -9,6 +11,15 @@
911
#include "py/gc.h"
1012
#include "lib/utils/pyexec.h"
1113

14+
#include "emscripten.h"
15+
16+
// TODO: make this work properly with emscripten
17+
#ifdef _WIN32
18+
#define PATHLIST_SEP_CHAR ';'
19+
#else
20+
#define PATHLIST_SEP_CHAR ':'
21+
#endif
22+
1223
void do_str(const char *src, mp_parse_input_kind_t input_kind) {
1324
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
1425
if (lex == NULL) {
@@ -38,6 +49,48 @@ static char heap[2048];
3849
void mp_js_init() {
3950
gc_init(heap, heap + sizeof(heap));
4051
mp_init();
52+
53+
char *home = getenv("HOME");
54+
char *path = getenv("MICROPYPATH");
55+
if (path == NULL) {
56+
#ifdef MICROPY_PY_SYS_PATH_DEFAULT
57+
path = MICROPY_PY_SYS_PATH_DEFAULT;
58+
#else
59+
path = "~/.micropython/lib:/usr/lib/micropython";
60+
#endif
61+
}
62+
mp_uint_t path_num = 1; // [0] is for current dir (or base dir of the script)
63+
for (char *p = path; p != NULL; p = strchr(p, PATHLIST_SEP_CHAR)) {
64+
path_num++;
65+
if (p != NULL) {
66+
p++;
67+
}
68+
}
69+
mp_obj_list_init(MP_OBJ_TO_PTR(mp_sys_path), path_num);
70+
mp_obj_t *path_items;
71+
mp_obj_list_get(mp_sys_path, &path_num, &path_items);
72+
path_items[0] = MP_OBJ_NEW_QSTR(MP_QSTR_);
73+
{
74+
char *p = path;
75+
for (mp_uint_t i = 1; i < path_num; i++) {
76+
char *p1 = strchr(p, PATHLIST_SEP_CHAR);
77+
if (p1 == NULL) {
78+
p1 = p + strlen(p);
79+
}
80+
if (p[0] == '~' && p[1] == '/' && home != NULL) {
81+
// Expand standalone ~ to $HOME
82+
int home_l = strlen(home);
83+
vstr_t vstr;
84+
vstr_init(&vstr, home_l + (p1 - p - 1) + 1);
85+
vstr_add_strn(&vstr, home, home_l);
86+
vstr_add_strn(&vstr, p + 1, p1 - p - 1);
87+
path_items[i] = mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
88+
} else {
89+
path_items[i] = MP_OBJ_NEW_QSTR(qstr_from_strn(p, p1 - p));
90+
}
91+
p = p1 + 1;
92+
}
93+
}
4194
}
4295

4396
void mp_js_run(const char * code) {
@@ -55,11 +108,53 @@ void gc_collect(void) {
55108
}
56109

57110
mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
58-
return NULL;
111+
// to do this by calling out to node.js code, we'll have to do something like
112+
// fs.readFileSync(filename, 'utf-8')
113+
// and then put that back into the emscripten heap, so that it's a pointer we can access from C
114+
// and then use that to produce a micropython lexer as above.
115+
116+
// eurgh this is disgusting, let's just implement this in JS
117+
118+
const char *code_buf = (const char *)EM_ASM_INT({
119+
try {
120+
var file = UTF8ToString($0);
121+
var fs = require('fs');
122+
123+
var code = fs.readFileSync(file, 'utf-8');
124+
ret = Runtime.stackAlloc((code.length << 2) + 1);
125+
writeStringToMemory(code, ret);
126+
return ret;
127+
} catch (e) {
128+
return 0;
129+
}
130+
}, filename);
131+
132+
if (code_buf == 0) {
133+
return NULL;
134+
} else {
135+
mp_lexer_t* lex = mp_lexer_new_from_str_len(qstr_from_str(filename), code_buf, strlen(code_buf), 0);
136+
return lex;
137+
}
59138
}
60139

61140
mp_import_stat_t mp_import_stat(const char *path) {
62-
return MP_IMPORT_STAT_NO_EXIST;
141+
int s = EM_ASM_INT({
142+
try {
143+
var fs = require('fs');
144+
var stat = fs.statSync(UTF8ToString($0));
145+
return stat.isDirectory() ? 1 : 0;
146+
} catch(e) {
147+
return -1;
148+
}
149+
}, path);
150+
151+
if (s == -1) {
152+
return MP_IMPORT_STAT_NO_EXIST;
153+
} else if (s == 1) {
154+
return MP_IMPORT_STAT_DIR;
155+
} else {
156+
return MP_IMPORT_STAT_FILE;
157+
}
63158
}
64159

65160
mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
@@ -82,161 +177,3 @@ void MP_WEAK __assert_func(const char *file, int line, const char *func, const c
82177
}
83178
#endif
84179

85-
#if MICROPY_MIN_USE_CORTEX_CPU
86-
87-
// this is a minimal IRQ and reset framework for any Cortex-M CPU
88-
89-
extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss;
90-
91-
void Reset_Handler(void) __attribute__((naked));
92-
void Reset_Handler(void) {
93-
// set stack pointer
94-
asm volatile ("ldr sp, =_estack");
95-
// copy .data section from flash to RAM
96-
for (uint32_t *src = &_sidata, *dest = &_sdata; dest < &_edata;) {
97-
*dest++ = *src++;
98-
}
99-
// zero out .bss section
100-
for (uint32_t *dest = &_sbss; dest < &_ebss;) {
101-
*dest++ = 0;
102-
}
103-
// jump to board initialisation
104-
void _start(void);
105-
_start();
106-
}
107-
108-
void Default_Handler(void) {
109-
for (;;) {
110-
}
111-
}
112-
113-
uint32_t isr_vector[] __attribute__((section(".isr_vector"))) = {
114-
(uint32_t)&_estack,
115-
(uint32_t)&Reset_Handler,
116-
(uint32_t)&Default_Handler, // NMI_Handler
117-
(uint32_t)&Default_Handler, // HardFault_Handler
118-
(uint32_t)&Default_Handler, // MemManage_Handler
119-
(uint32_t)&Default_Handler, // BusFault_Handler
120-
(uint32_t)&Default_Handler, // UsageFault_Handler
121-
0,
122-
0,
123-
0,
124-
0,
125-
(uint32_t)&Default_Handler, // SVC_Handler
126-
(uint32_t)&Default_Handler, // DebugMon_Handler
127-
0,
128-
(uint32_t)&Default_Handler, // PendSV_Handler
129-
(uint32_t)&Default_Handler, // SysTick_Handler
130-
};
131-
132-
void _start(void) {
133-
// when we get here: stack is initialised, bss is clear, data is copied
134-
135-
// SCB->CCR: enable 8-byte stack alignment for IRQ handlers, in accord with EABI
136-
*((volatile uint32_t*)0xe000ed14) |= 1 << 9;
137-
138-
// initialise the cpu and peripherals
139-
#if MICROPY_MIN_USE_STM32_MCU
140-
void stm32_init(void);
141-
stm32_init();
142-
#endif
143-
144-
// now that we have a basic system up and running we can call main
145-
main(0, NULL);
146-
147-
// we must not return
148-
for (;;) {
149-
}
150-
}
151-
152-
#endif
153-
154-
#if MICROPY_MIN_USE_STM32_MCU
155-
156-
// this is minimal set-up code for an STM32 MCU
157-
158-
typedef struct {
159-
volatile uint32_t CR;
160-
volatile uint32_t PLLCFGR;
161-
volatile uint32_t CFGR;
162-
volatile uint32_t CIR;
163-
uint32_t _1[8];
164-
volatile uint32_t AHB1ENR;
165-
volatile uint32_t AHB2ENR;
166-
volatile uint32_t AHB3ENR;
167-
uint32_t _2;
168-
volatile uint32_t APB1ENR;
169-
volatile uint32_t APB2ENR;
170-
} periph_rcc_t;
171-
172-
typedef struct {
173-
volatile uint32_t MODER;
174-
volatile uint32_t OTYPER;
175-
volatile uint32_t OSPEEDR;
176-
volatile uint32_t PUPDR;
177-
volatile uint32_t IDR;
178-
volatile uint32_t ODR;
179-
volatile uint16_t BSRRL;
180-
volatile uint16_t BSRRH;
181-
volatile uint32_t LCKR;
182-
volatile uint32_t AFR[2];
183-
} periph_gpio_t;
184-
185-
typedef struct {
186-
volatile uint32_t SR;
187-
volatile uint32_t DR;
188-
volatile uint32_t BRR;
189-
volatile uint32_t CR1;
190-
} periph_uart_t;
191-
192-
#define USART1 ((periph_uart_t*) 0x40011000)
193-
#define GPIOA ((periph_gpio_t*) 0x40020000)
194-
#define GPIOB ((periph_gpio_t*) 0x40020400)
195-
#define RCC ((periph_rcc_t*) 0x40023800)
196-
197-
// simple GPIO interface
198-
#define GPIO_MODE_IN (0)
199-
#define GPIO_MODE_OUT (1)
200-
#define GPIO_MODE_ALT (2)
201-
#define GPIO_PULL_NONE (0)
202-
#define GPIO_PULL_UP (0)
203-
#define GPIO_PULL_DOWN (1)
204-
void gpio_init(periph_gpio_t *gpio, int pin, int mode, int pull, int alt) {
205-
gpio->MODER = (gpio->MODER & ~(3 << (2 * pin))) | (mode << (2 * pin));
206-
// OTYPER is left as default push-pull
207-
// OSPEEDR is left as default low speed
208-
gpio->PUPDR = (gpio->PUPDR & ~(3 << (2 * pin))) | (pull << (2 * pin));
209-
gpio->AFR[pin >> 3] = (gpio->AFR[pin >> 3] & ~(15 << (4 * (pin & 7)))) | (alt << (4 * (pin & 7)));
210-
}
211-
#define gpio_get(gpio, pin) ((gpio->IDR >> (pin)) & 1)
212-
#define gpio_set(gpio, pin, value) do { gpio->ODR = (gpio->ODR & ~(1 << (pin))) | (value << pin); } while (0)
213-
#define gpio_low(gpio, pin) do { gpio->BSRRH = (1 << (pin)); } while (0)
214-
#define gpio_high(gpio, pin) do { gpio->BSRRL = (1 << (pin)); } while (0)
215-
216-
void stm32_init(void) {
217-
// basic MCU config
218-
RCC->CR |= (uint32_t)0x00000001; // set HSION
219-
RCC->CFGR = 0x00000000; // reset all
220-
RCC->CR &= (uint32_t)0xfef6ffff; // reset HSEON, CSSON, PLLON
221-
RCC->PLLCFGR = 0x24003010; // reset PLLCFGR
222-
RCC->CR &= (uint32_t)0xfffbffff; // reset HSEBYP
223-
RCC->CIR = 0x00000000; // disable IRQs
224-
225-
// leave the clock as-is (internal 16MHz)
226-
227-
// enable GPIO clocks
228-
RCC->AHB1ENR |= 0x00000003; // GPIOAEN, GPIOBEN
229-
230-
// turn on an LED! (on pyboard it's the red one)
231-
gpio_init(GPIOA, 13, GPIO_MODE_OUT, GPIO_PULL_NONE, 0);
232-
gpio_high(GPIOA, 13);
233-
234-
// enable UART1 at 9600 baud (TX=B6, RX=B7)
235-
gpio_init(GPIOB, 6, GPIO_MODE_ALT, GPIO_PULL_NONE, 7);
236-
gpio_init(GPIOB, 7, GPIO_MODE_ALT, GPIO_PULL_NONE, 7);
237-
RCC->APB2ENR |= 0x00000010; // USART1EN
238-
USART1->BRR = (104 << 4) | 3; // 16MHz/(16*104.1875) = 9598 baud
239-
USART1->CR1 = 0x0000200c; // USART enable, tx enable, rx enable
240-
}
241-
242-
#endif

emscripten/server.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ if (argv._.length == 1) {
1818
var code = argv.c;
1919
mpy.run(code);
2020
} else if (argv.m) {
21-
// TODO: make object loading work properly with Emscripten
21+
var code = 'import ' + argv.m;
22+
mpy.run(code);
2223
} else if (argv._.length == 0) {
2324
console.log("MicroPython on asmjs with emscripten");
2425
console.log("Type \"help()\" for more information.");

0 commit comments

Comments
 (0)
0