From 774c8d0f443729c00a20f75bfa7631ede56934e7 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Sat, 20 Jan 2024 12:59:04 +0100 Subject: [PATCH 01/27] added dpoly() function --- ports/sh/modgint.c | 412 +++++++++++++++++++++++---------------------- 1 file changed, 207 insertions(+), 205 deletions(-) diff --git a/ports/sh/modgint.c b/ports/sh/modgint.c index 768f22a0b..725faaca0 100644 --- a/ports/sh/modgint.c +++ b/ports/sh/modgint.c @@ -9,138 +9,120 @@ // considered relevant for high-level Python development). //--- -#include "py/runtime.h" +#include "console.h" #include "py/objtuple.h" +#include "py/runtime.h" #include #include +#include +#include + void pe_enter_graphics_mode(void); -#define FUN_0(NAME) \ - MP_DEFINE_CONST_FUN_OBJ_0(modgint_ ## NAME ## _obj, modgint_ ## NAME) -#define FUN_1(NAME) \ - MP_DEFINE_CONST_FUN_OBJ_1(modgint_ ## NAME ## _obj, modgint_ ## NAME) -#define FUN_2(NAME) \ - MP_DEFINE_CONST_FUN_OBJ_2(modgint_ ## NAME ## _obj, modgint_ ## NAME) -#define FUN_3(NAME) \ - MP_DEFINE_CONST_FUN_OBJ_3(modgint_ ## NAME ## _obj, modgint_ ## NAME) -#define FUN_VAR(NAME, MIN) \ - MP_DEFINE_CONST_FUN_OBJ_VAR(modgint_ ## NAME ## _obj, MIN, \ - modgint_ ## NAME) -#define FUN_BETWEEN(NAME, MIN, MAX) \ - MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modgint_ ## NAME ## _obj, MIN, MAX, \ - modgint_ ## NAME) +#define FUN_0(NAME) \ + MP_DEFINE_CONST_FUN_OBJ_0(modgint_##NAME##_obj, modgint_##NAME) +#define FUN_1(NAME) \ + MP_DEFINE_CONST_FUN_OBJ_1(modgint_##NAME##_obj, modgint_##NAME) +#define FUN_2(NAME) \ + MP_DEFINE_CONST_FUN_OBJ_2(modgint_##NAME##_obj, modgint_##NAME) +#define FUN_3(NAME) \ + MP_DEFINE_CONST_FUN_OBJ_3(modgint_##NAME##_obj, modgint_##NAME) +#define FUN_VAR(NAME, MIN) \ + MP_DEFINE_CONST_FUN_OBJ_VAR(modgint_##NAME##_obj, MIN, modgint_##NAME) +#define FUN_BETWEEN(NAME, MIN, MAX) \ + MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modgint_##NAME##_obj, MIN, MAX, \ + modgint_##NAME) -STATIC mp_obj_t modgint___init__(void) -{ - pe_enter_graphics_mode(); - dclear(C_WHITE); - return mp_const_none; +STATIC mp_obj_t modgint___init__(void) { + pe_enter_graphics_mode(); + dclear(C_WHITE); + return mp_const_none; } /* */ STATIC qstr const key_event_fields[] = { - MP_QSTR_time, - MP_QSTR_mod, - MP_QSTR_shift, - MP_QSTR_alpha, - MP_QSTR_type, - MP_QSTR_key, + MP_QSTR_time, MP_QSTR_mod, MP_QSTR_shift, + MP_QSTR_alpha, MP_QSTR_type, MP_QSTR_key, }; -STATIC mp_obj_t mk_key_event(key_event_t ev) -{ - mp_obj_t items[] = { - mp_obj_new_int(ev.time), - mp_obj_new_bool(ev.mod), - mp_obj_new_bool(ev.shift), - mp_obj_new_bool(ev.alpha), - mp_obj_new_int(ev.type), - mp_obj_new_int(ev.key), - }; - return mp_obj_new_attrtuple(key_event_fields, 6, items); +STATIC mp_obj_t mk_key_event(key_event_t ev) { + mp_obj_t items[] = { + mp_obj_new_int(ev.time), mp_obj_new_bool(ev.mod), + mp_obj_new_bool(ev.shift), mp_obj_new_bool(ev.alpha), + mp_obj_new_int(ev.type), mp_obj_new_int(ev.key), + }; + return mp_obj_new_attrtuple(key_event_fields, 6, items); } -STATIC mp_obj_t modgint_pollevent(void) -{ - key_event_t ev = pollevent(); - return mk_key_event(ev); +STATIC mp_obj_t modgint_pollevent(void) { + key_event_t ev = pollevent(); + return mk_key_event(ev); } // TODO: waitevent: timeout parameter? -STATIC mp_obj_t modgint_clearevents(void) -{ - clearevents(); - return mp_const_none; +STATIC mp_obj_t modgint_clearevents(void) { + clearevents(); + return mp_const_none; } -STATIC mp_obj_t modgint_cleareventflips(void) -{ - cleareventflips(); - return mp_const_none; +STATIC mp_obj_t modgint_cleareventflips(void) { + cleareventflips(); + return mp_const_none; } -STATIC mp_obj_t modgint_keydown(mp_obj_t arg1) -{ - mp_int_t key = mp_obj_get_int(arg1); - bool down = keydown(key) != 0; - return mp_obj_new_bool(down); +STATIC mp_obj_t modgint_keydown(mp_obj_t arg1) { + mp_int_t key = mp_obj_get_int(arg1); + bool down = keydown(key) != 0; + return mp_obj_new_bool(down); } -STATIC mp_obj_t modgint_keydown_all(size_t n, mp_obj_t const *args) -{ - bool down = true; - for(size_t i = 0; i < n; i++) - down &= keydown(mp_obj_get_int(args[i])) != 0; - return mp_obj_new_bool(down); +STATIC mp_obj_t modgint_keydown_all(size_t n, mp_obj_t const *args) { + bool down = true; + for (size_t i = 0; i < n; i++) + down &= keydown(mp_obj_get_int(args[i])) != 0; + return mp_obj_new_bool(down); } -STATIC mp_obj_t modgint_keydown_any(size_t n, mp_obj_t const *args) -{ - bool down = false; - for(size_t i = 0; i < n; i++) - down |= keydown(mp_obj_get_int(args[i])) != 0; - return mp_obj_new_bool(down); +STATIC mp_obj_t modgint_keydown_any(size_t n, mp_obj_t const *args) { + bool down = false; + for (size_t i = 0; i < n; i++) + down |= keydown(mp_obj_get_int(args[i])) != 0; + return mp_obj_new_bool(down); } -STATIC mp_obj_t modgint_keypressed(mp_obj_t arg1) -{ - mp_int_t key = mp_obj_get_int(arg1); - return mp_obj_new_bool(keypressed(key) != 0); +STATIC mp_obj_t modgint_keypressed(mp_obj_t arg1) { + mp_int_t key = mp_obj_get_int(arg1); + return mp_obj_new_bool(keypressed(key) != 0); } -STATIC mp_obj_t modgint_keyreleased(mp_obj_t arg1) -{ - mp_int_t key = mp_obj_get_int(arg1); - return mp_obj_new_bool(keyreleased(key) != 0); +STATIC mp_obj_t modgint_keyreleased(mp_obj_t arg1) { + mp_int_t key = mp_obj_get_int(arg1); + return mp_obj_new_bool(keyreleased(key) != 0); } -STATIC mp_obj_t modgint_getkey(void) -{ - key_event_t ev = getkey(); - return mk_key_event(ev); +STATIC mp_obj_t modgint_getkey(void) { + key_event_t ev = getkey(); + return mk_key_event(ev); } // TODO: getkey_opt: timeout parameter? -STATIC mp_obj_t modgint_getkey_opt(mp_obj_t arg1) -{ - int options = mp_obj_get_int(arg1); - key_event_t ev = getkey_opt(options, NULL); - return mk_key_event(ev); +STATIC mp_obj_t modgint_getkey_opt(mp_obj_t arg1) { + int options = mp_obj_get_int(arg1); + key_event_t ev = getkey_opt(options, NULL); + return mk_key_event(ev); } -STATIC mp_obj_t modgint_keycode_function(mp_obj_t arg1) -{ - int keycode = mp_obj_get_int(arg1); - return MP_OBJ_NEW_SMALL_INT(keycode_function(keycode)); +STATIC mp_obj_t modgint_keycode_function(mp_obj_t arg1) { + int keycode = mp_obj_get_int(arg1); + return MP_OBJ_NEW_SMALL_INT(keycode_function(keycode)); } -STATIC mp_obj_t modgint_keycode_digit(mp_obj_t arg1) -{ - int keycode = mp_obj_get_int(arg1); - return MP_OBJ_NEW_SMALL_INT(keycode_digit(keycode)); +STATIC mp_obj_t modgint_keycode_digit(mp_obj_t arg1) { + int keycode = mp_obj_get_int(arg1); + return MP_OBJ_NEW_SMALL_INT(keycode_digit(keycode)); } FUN_0(clearevents); @@ -152,152 +134,168 @@ FUN_VAR(keydown_any, 0); FUN_1(keypressed); FUN_1(keyreleased); FUN_0(getkey); -FUN_1/*2*/(getkey_opt); +FUN_1 /*2*/ (getkey_opt); FUN_1(keycode_function); FUN_1(keycode_digit); /* */ #ifdef FXCG50 -STATIC mp_obj_t modgint_C_RGB(mp_obj_t arg1, mp_obj_t arg2, mp_obj_t arg3) -{ - mp_int_t r = mp_obj_get_int(arg1); - mp_int_t g = mp_obj_get_int(arg2); - mp_int_t b = mp_obj_get_int(arg3); - return MP_OBJ_NEW_SMALL_INT(C_RGB(r, g, b)); +STATIC mp_obj_t modgint_C_RGB(mp_obj_t arg1, mp_obj_t arg2, mp_obj_t arg3) { + mp_int_t r = mp_obj_get_int(arg1); + mp_int_t g = mp_obj_get_int(arg2); + mp_int_t b = mp_obj_get_int(arg3); + return MP_OBJ_NEW_SMALL_INT(C_RGB(r, g, b)); } #endif -STATIC mp_obj_t modgint_dclear(mp_obj_t arg1) -{ - mp_int_t color = mp_obj_get_int(arg1); - dclear(color); - return mp_const_none; +STATIC mp_obj_t modgint_dclear(mp_obj_t arg1) { + mp_int_t color = mp_obj_get_int(arg1); + dclear(color); + return mp_const_none; } -STATIC mp_obj_t modgint_dupdate(void) -{ - pe_enter_graphics_mode(); - dupdate(); - return mp_const_none; +STATIC mp_obj_t modgint_dupdate(void) { + pe_enter_graphics_mode(); + dupdate(); + return mp_const_none; } -STATIC mp_obj_t modgint_drect(size_t n, mp_obj_t const *args) -{ - mp_int_t x1 = mp_obj_get_int(args[0]); - mp_int_t y1 = mp_obj_get_int(args[1]); - mp_int_t x2 = mp_obj_get_int(args[2]); - mp_int_t y2 = mp_obj_get_int(args[3]); - mp_int_t color = mp_obj_get_int(args[4]); - drect(x1, y1, x2, y2, color); - return mp_const_none; +STATIC mp_obj_t modgint_drect(size_t n, mp_obj_t const *args) { + mp_int_t x1 = mp_obj_get_int(args[0]); + mp_int_t y1 = mp_obj_get_int(args[1]); + mp_int_t x2 = mp_obj_get_int(args[2]); + mp_int_t y2 = mp_obj_get_int(args[3]); + mp_int_t color = mp_obj_get_int(args[4]); + drect(x1, y1, x2, y2, color); + return mp_const_none; } -STATIC mp_obj_t modgint_drect_border(size_t n, mp_obj_t const *args) -{ - mp_int_t x1 = mp_obj_get_int(args[0]); - mp_int_t y1 = mp_obj_get_int(args[1]); - mp_int_t x2 = mp_obj_get_int(args[2]); - mp_int_t y2 = mp_obj_get_int(args[3]); - mp_int_t fill_color = mp_obj_get_int(args[4]); - mp_int_t border_width = mp_obj_get_int(args[5]); - mp_int_t border_color = mp_obj_get_int(args[6]); - drect_border(x1, y1, x2, y2, fill_color, border_width, border_color); - return mp_const_none; +STATIC mp_obj_t modgint_drect_border(size_t n, mp_obj_t const *args) { + mp_int_t x1 = mp_obj_get_int(args[0]); + mp_int_t y1 = mp_obj_get_int(args[1]); + mp_int_t x2 = mp_obj_get_int(args[2]); + mp_int_t y2 = mp_obj_get_int(args[3]); + mp_int_t fill_color = mp_obj_get_int(args[4]); + mp_int_t border_width = mp_obj_get_int(args[5]); + mp_int_t border_color = mp_obj_get_int(args[6]); + drect_border(x1, y1, x2, y2, fill_color, border_width, border_color); + return mp_const_none; } -STATIC mp_obj_t modgint_dpixel(mp_obj_t arg1, mp_obj_t arg2, mp_obj_t arg3) -{ - mp_int_t x = mp_obj_get_int(arg1); - mp_int_t y = mp_obj_get_int(arg2); - mp_int_t color = mp_obj_get_int(arg3); - dpixel(x, y, color); - return mp_const_none; +STATIC mp_obj_t modgint_dpixel(mp_obj_t arg1, mp_obj_t arg2, mp_obj_t arg3) { + mp_int_t x = mp_obj_get_int(arg1); + mp_int_t y = mp_obj_get_int(arg2); + mp_int_t color = mp_obj_get_int(arg3); + dpixel(x, y, color); + return mp_const_none; } -STATIC mp_obj_t modgint_dgetpixel(mp_obj_t arg1, mp_obj_t arg2) -{ - mp_int_t x = mp_obj_get_int(arg1); - mp_int_t y = mp_obj_get_int(arg2); - return MP_OBJ_NEW_SMALL_INT(dgetpixel(x, y)); +STATIC mp_obj_t modgint_dgetpixel(mp_obj_t arg1, mp_obj_t arg2) { + mp_int_t x = mp_obj_get_int(arg1); + mp_int_t y = mp_obj_get_int(arg2); + return MP_OBJ_NEW_SMALL_INT(dgetpixel(x, y)); } -STATIC mp_obj_t modgint_dline(size_t n, mp_obj_t const *args) -{ - mp_int_t x1 = mp_obj_get_int(args[0]); - mp_int_t y1 = mp_obj_get_int(args[1]); - mp_int_t x2 = mp_obj_get_int(args[2]); - mp_int_t y2 = mp_obj_get_int(args[3]); - mp_int_t color = mp_obj_get_int(args[4]); - dline(x1, y1, x2, y2, color); - return mp_const_none; +STATIC mp_obj_t modgint_dline(size_t n, mp_obj_t const *args) { + mp_int_t x1 = mp_obj_get_int(args[0]); + mp_int_t y1 = mp_obj_get_int(args[1]); + mp_int_t x2 = mp_obj_get_int(args[2]); + mp_int_t y2 = mp_obj_get_int(args[3]); + mp_int_t color = mp_obj_get_int(args[4]); + dline(x1, y1, x2, y2, color); + return mp_const_none; } -STATIC mp_obj_t modgint_dhline(mp_obj_t arg1, mp_obj_t arg2) -{ - mp_int_t y = mp_obj_get_int(arg1); - mp_int_t color = mp_obj_get_int(arg2); - dhline(y, color); - return mp_const_none; +STATIC mp_obj_t modgint_dhline(mp_obj_t arg1, mp_obj_t arg2) { + mp_int_t y = mp_obj_get_int(arg1); + mp_int_t color = mp_obj_get_int(arg2); + dhline(y, color); + return mp_const_none; } -STATIC mp_obj_t modgint_dvline(mp_obj_t arg1, mp_obj_t arg2) -{ - mp_int_t x = mp_obj_get_int(arg1); - mp_int_t color = mp_obj_get_int(arg2); - dvline(x, color); - return mp_const_none; +STATIC mp_obj_t modgint_dvline(mp_obj_t arg1, mp_obj_t arg2) { + mp_int_t x = mp_obj_get_int(arg1); + mp_int_t color = mp_obj_get_int(arg2); + dvline(x, color); + return mp_const_none; } -STATIC mp_obj_t modgint_dcircle(size_t n_args, const mp_obj_t *args) -{ - mp_int_t x = mp_obj_get_int(args[0]); - mp_int_t y = mp_obj_get_int(args[1]); - mp_int_t r = mp_obj_get_int(args[2]); - mp_int_t fill = mp_obj_get_int(args[3]); - mp_int_t border = mp_obj_get_int(args[4]); +STATIC mp_obj_t modgint_dpoly(mp_obj_t points, mp_obj_t fill, mp_obj_t border) { - dcircle(x, y, r, fill, border); + mp_uint_t nbitems; + mp_obj_t *items; + + mp_obj_list_get(points, &nbitems, &items); + + unsigned int len = nbitems / 2; + int *x = (int *)malloc(len * sizeof(int)); + int *y = (int *)malloc(len * sizeof(int)); + + if (!x || !y) return mp_const_none; + + for (unsigned int u = 0; u < len; u++) { + x[u] = mp_obj_get_int(items[u * 2]); + y[u] = mp_obj_get_int(items[u * 2 + 1]); + } + + mp_int_t fillcolor = mp_obj_get_int(fill); + mp_int_t bordercolor = mp_obj_get_int(border); + + dpoly(x, y, len, fillcolor, bordercolor); + + free(x); + free(y); + + return mp_const_none; } -STATIC mp_obj_t modgint_dellipse(size_t n_args, const mp_obj_t *args) -{ - mp_int_t x1 = mp_obj_get_int(args[0]); - mp_int_t y1 = mp_obj_get_int(args[1]); - mp_int_t x2 = mp_obj_get_int(args[2]); - mp_int_t y2 = mp_obj_get_int(args[3]); - mp_int_t fill = mp_obj_get_int(args[4]); - mp_int_t border = mp_obj_get_int(args[5]); +STATIC mp_obj_t modgint_dcircle(size_t n_args, const mp_obj_t *args) { + mp_int_t x = mp_obj_get_int(args[0]); + mp_int_t y = mp_obj_get_int(args[1]); + mp_int_t r = mp_obj_get_int(args[2]); + mp_int_t fill = mp_obj_get_int(args[3]); + mp_int_t border = mp_obj_get_int(args[4]); - dellipse(x1, y1, x2, y2, fill, border); - return mp_const_none; + dcircle(x, y, r, fill, border); + return mp_const_none; +} + +STATIC mp_obj_t modgint_dellipse(size_t n_args, const mp_obj_t *args) { + mp_int_t x1 = mp_obj_get_int(args[0]); + mp_int_t y1 = mp_obj_get_int(args[1]); + mp_int_t x2 = mp_obj_get_int(args[2]); + mp_int_t y2 = mp_obj_get_int(args[3]); + mp_int_t fill = mp_obj_get_int(args[4]); + mp_int_t border = mp_obj_get_int(args[5]); + + dellipse(x1, y1, x2, y2, fill, border); + return mp_const_none; } // TODO: modgint: Font management? -STATIC mp_obj_t modgint_dtext_opt(size_t n, mp_obj_t const *args) -{ - mp_int_t x = mp_obj_get_int(args[0]); - mp_int_t y = mp_obj_get_int(args[1]); - mp_int_t fg = mp_obj_get_int(args[2]); - mp_int_t bg = mp_obj_get_int(args[3]); - mp_int_t halign = mp_obj_get_int(args[4]); - mp_int_t valign = mp_obj_get_int(args[5]); - char const *str = mp_obj_str_get_str(args[6]); - mp_int_t size = mp_obj_get_int(args[7]); - dtext_opt(x, y, fg, bg, halign, valign, str, size); - return mp_const_none; +STATIC mp_obj_t modgint_dtext_opt(size_t n, mp_obj_t const *args) { + mp_int_t x = mp_obj_get_int(args[0]); + mp_int_t y = mp_obj_get_int(args[1]); + mp_int_t fg = mp_obj_get_int(args[2]); + mp_int_t bg = mp_obj_get_int(args[3]); + mp_int_t halign = mp_obj_get_int(args[4]); + mp_int_t valign = mp_obj_get_int(args[5]); + char const *str = mp_obj_str_get_str(args[6]); + mp_int_t size = mp_obj_get_int(args[7]); + dtext_opt(x, y, fg, bg, halign, valign, str, size); + return mp_const_none; } -STATIC mp_obj_t modgint_dtext(size_t n, mp_obj_t const *args) -{ - mp_int_t x = mp_obj_get_int(args[0]); - mp_int_t y = mp_obj_get_int(args[1]); - mp_int_t fg = mp_obj_get_int(args[2]); - char const *str = mp_obj_str_get_str(args[3]); - dtext(x, y, fg, str); - return mp_const_none; +STATIC mp_obj_t modgint_dtext(size_t n, mp_obj_t const *args) { + mp_int_t x = mp_obj_get_int(args[0]); + mp_int_t y = mp_obj_get_int(args[1]); + mp_int_t fg = mp_obj_get_int(args[2]); + char const *str = mp_obj_str_get_str(args[3]); + dtext(x, y, fg, str); + return mp_const_none; } FUN_0(__init__); @@ -314,6 +312,7 @@ FUN_2(dgetpixel); FUN_BETWEEN(dline, 5, 5); FUN_2(dhline); FUN_2(dvline); +FUN_3(dpoly); FUN_BETWEEN(dcircle, 5, 5); FUN_BETWEEN(dellipse, 6, 6); FUN_BETWEEN(dtext_opt, 8, 8); @@ -322,13 +321,15 @@ FUN_BETWEEN(dtext, 4, 4); /* Module definition */ // Helper: define object "modgint_F_obj" as object "F" in the module -#define OBJ(F) {MP_ROM_QSTR(MP_QSTR_ ## F), MP_ROM_PTR(&modgint_ ## F ## _obj)} +#define OBJ(F) \ + { MP_ROM_QSTR(MP_QSTR_##F), MP_ROM_PTR(&modgint_##F##_obj) } // Helper: define small integer constant "I" as "I" in the module -#define INT(I) {MP_ROM_QSTR(MP_QSTR_ ## I), MP_OBJ_NEW_SMALL_INT(I)} +#define INT(I) \ + { MP_ROM_QSTR(MP_QSTR_##I), MP_OBJ_NEW_SMALL_INT(I) } STATIC const mp_rom_map_elem_t modgint_module_globals_table[] = { - { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_gint) }, + {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_gint)}, OBJ(__init__), /* */ @@ -469,16 +470,17 @@ STATIC const mp_rom_map_elem_t modgint_module_globals_table[] = { OBJ(dline), OBJ(dhline), OBJ(dvline), + OBJ(dpoly), OBJ(dcircle), OBJ(dellipse), OBJ(dtext_opt), OBJ(dtext), }; -STATIC MP_DEFINE_CONST_DICT( - modgint_module_globals, modgint_module_globals_table); +STATIC MP_DEFINE_CONST_DICT(modgint_module_globals, + modgint_module_globals_table); const mp_obj_module_t modgint_module = { - .base = { &mp_type_module }, + .base = {&mp_type_module}, .globals = (mp_obj_dict_t *)&modgint_module_globals, }; From c207c7b6f73f2f1bc39fe138bd4691e53f082a20 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Mon, 29 Jan 2024 07:43:24 +0100 Subject: [PATCH 02/27] start NW support with dedicated modules (only for fxCG50) --- ports/fx9860g3/Makefile | 2 +- ports/fx9860g3/PoliceNW.png | Bin 0 -> 1419 bytes ports/fx9860g3/fxconv-metadata.txt | 9 ++ ports/fxcg50/Makefile | 2 +- ports/fxcg50/PoliceNW.png | Bin 0 -> 1419 bytes ports/fxcg50/fxconv-metadata.txt | 9 ++ ports/sh/Makefile | 2 + ports/sh/modkandinsky.c | 239 +++++++++++++++++++++++++++++ ports/sh/mpconfigport.h | 125 +++++++-------- 9 files changed, 325 insertions(+), 63 deletions(-) create mode 100644 ports/fx9860g3/PoliceNW.png create mode 100644 ports/fxcg50/PoliceNW.png create mode 100644 ports/sh/modkandinsky.c diff --git a/ports/fx9860g3/Makefile b/ports/fx9860g3/Makefile index a49a16024..aaefd7a6f 100644 --- a/ports/fx9860g3/Makefile +++ b/ports/fx9860g3/Makefile @@ -5,7 +5,7 @@ SH_LDFLAGS := -T fx9860g.ld -ljustui-fx -lm -lgint-fx -lc -lgint-fx -lgcc SH_ASSETS := \ img_fkeys_main.png img_modifier_states.png \ - font_5x7.png font_4x4.png font_4x6.png + font_5x7.png font_4x4.png font_4x6.png PoliceNW.png SH_METADATA := fxconv-metadata.txt SH_CONVFLAGS := --fx diff --git a/ports/fx9860g3/PoliceNW.png b/ports/fx9860g3/PoliceNW.png new file mode 100644 index 0000000000000000000000000000000000000000..b11e4159c55c99dc41bf6bf6db1756ddad8a3dc0 GIT binary patch literal 1419 zcmV;61$6p}P)003YJ0{{R3t@(+o00001b5ch_0Itp) z=>Px#3{Xr|MgRZ*%*@OH00960|A2LKga7~l1awkPQvm<}|FbPXhyVZu9Z5t%RA_2`S;c^%Y`Y8aW#vx*s&$G9VbpeQxYB0n4qbMXx9Vne zs$=X1S&A`o69ORenn_6zIe2^Hk{SQCLG$q>x94aBSg(&y)Z zBopGP5D+{7O*Ei@+Gcd{NlXqUVjwF(Q)9(=c?BREj>KlMATcD0Cxeo1y^}WvY;y#S z4}oGYV6Zjkl7l&` z%XZG&zGHH}-QKCogg|8+QwxzjAGBga5cOU}K(sz7dYc$J)wM&^q=2o@=4gCI%6xPV z2sZ_wK#Lp=?Zd|?AB){1av0%&tX~31V&OSJzMYo=<6A-t*j5DqH33xxg(z$RSwz%+ zF6#+^&QP#g={#VY0j67CL1U-s3SI*QECy3=@fc8_CIRf5?ExhLR5`_}y5Y zfhVD(`L)3)XA0>0(aWszxW(Gc7&L=EbskCF`V^7GnxUJ+Ypnq^k;G5)ZKcKH^Y2h@ zE`KAN`R4MGGYJHrQo}a)Q;oH`a577+ZZ~fa#949g=O)>AclBQobqfNGWLKxx8qkga zf9C-m;VD3nPXPM}H>o57Ft{*4bgmAN-7Nt21y2OTtu?Yw0Yc6{4d`9~#r3KH1**M~ zgfP?wgKQAiuSAb>Aq#+{6YgzhBoi52P6u* z2&lhT42;BqU~6e4zWBRmwB4I@fE9qANPp&~BCU{x>e&$T_UchJd*8(`a?E!|rz%=1%9Z*GiKp4&dr6<>OfaakRz&;H8 zhUtl;>}I-YT7R>MQ-CV80^DU62@nzn0TrvX5R-tgStCG)RVm&iAT<&gNwmr5bTo}L zt@yAjfZWY!S2#Snej~u%Y2D52$NF!fDOOx5MA%mWGFkJ0yW9=PMa^V0CRw--@N5d` Z%0B|UtM|%P`W*lO002ovPDHLkV1hn_eMkTR literal 0 HcmV?d00001 diff --git a/ports/fx9860g3/fxconv-metadata.txt b/ports/fx9860g3/fxconv-metadata.txt index ca6799fb6..587564b75 100644 --- a/ports/fx9860g3/fxconv-metadata.txt +++ b/ports/fx9860g3/fxconv-metadata.txt @@ -31,3 +31,12 @@ font_4x6.png: grid.padding: 1 grid.border: 0 proportional: true + +PoliceNW.png: + name: numworks + type: font + charset: print + grid.size: 10x16 + grid.padding: 0 + grid.border: 0 + proportional: false diff --git a/ports/fxcg50/Makefile b/ports/fxcg50/Makefile index 5cac3523a..8f3e96d49 100644 --- a/ports/fxcg50/Makefile +++ b/ports/fxcg50/Makefile @@ -3,7 +3,7 @@ include ../../py/mkenv.mk SH_CFLAGS := -DFXCG50 SH_LDFLAGS := -T fxcg50.ld -ljustui-cg -lm -lgint-cg -lc -lgint-cg -lgcc -SH_ASSETS := img_modifier_states.png font_9.png font_13.png font_19.png +SH_ASSETS := img_modifier_states.png font_9.png font_13.png font_19.png PoliceNW.png SH_METADATA := fxconv-metadata.txt SH_CONVFLAGS := --cg diff --git a/ports/fxcg50/PoliceNW.png b/ports/fxcg50/PoliceNW.png new file mode 100644 index 0000000000000000000000000000000000000000..b11e4159c55c99dc41bf6bf6db1756ddad8a3dc0 GIT binary patch literal 1419 zcmV;61$6p}P)003YJ0{{R3t@(+o00001b5ch_0Itp) z=>Px#3{Xr|MgRZ*%*@OH00960|A2LKga7~l1awkPQvm<}|FbPXhyVZu9Z5t%RA_2`S;c^%Y`Y8aW#vx*s&$G9VbpeQxYB0n4qbMXx9Vne zs$=X1S&A`o69ORenn_6zIe2^Hk{SQCLG$q>x94aBSg(&y)Z zBopGP5D+{7O*Ei@+Gcd{NlXqUVjwF(Q)9(=c?BREj>KlMATcD0Cxeo1y^}WvY;y#S z4}oGYV6Zjkl7l&` z%XZG&zGHH}-QKCogg|8+QwxzjAGBga5cOU}K(sz7dYc$J)wM&^q=2o@=4gCI%6xPV z2sZ_wK#Lp=?Zd|?AB){1av0%&tX~31V&OSJzMYo=<6A-t*j5DqH33xxg(z$RSwz%+ zF6#+^&QP#g={#VY0j67CL1U-s3SI*QECy3=@fc8_CIRf5?ExhLR5`_}y5Y zfhVD(`L)3)XA0>0(aWszxW(Gc7&L=EbskCF`V^7GnxUJ+Ypnq^k;G5)ZKcKH^Y2h@ zE`KAN`R4MGGYJHrQo}a)Q;oH`a577+ZZ~fa#949g=O)>AclBQobqfNGWLKxx8qkga zf9C-m;VD3nPXPM}H>o57Ft{*4bgmAN-7Nt21y2OTtu?Yw0Yc6{4d`9~#r3KH1**M~ zgfP?wgKQAiuSAb>Aq#+{6YgzhBoi52P6u* z2&lhT42;BqU~6e4zWBRmwB4I@fE9qANPp&~BCU{x>e&$T_UchJd*8(`a?E!|rz%=1%9Z*GiKp4&dr6<>OfaakRz&;H8 zhUtl;>}I-YT7R>MQ-CV80^DU62@nzn0TrvX5R-tgStCG)RVm&iAT<&gNwmr5bTo}L zt@yAjfZWY!S2#Snej~u%Y2D52$NF!fDOOx5MA%mWGFkJ0yW9=PMa^V0CRw--@N5d` Z%0B|UtM|%P`W*lO002ovPDHLkV1hn_eMkTR literal 0 HcmV?d00001 diff --git a/ports/fxcg50/fxconv-metadata.txt b/ports/fxcg50/fxconv-metadata.txt index 47bff3b83..67e505288 100644 --- a/ports/fxcg50/fxconv-metadata.txt +++ b/ports/fxcg50/fxconv-metadata.txt @@ -29,3 +29,12 @@ font_19.png: grid.padding: 0 grid.border: 0 proportional: true + +PoliceNW.png: + name: numworks + type: font + charset: print + grid.size: 10x16 + grid.padding: 0 + grid.border: 0 + proportional: false \ No newline at end of file diff --git a/ports/sh/Makefile b/ports/sh/Makefile index b29325617..af214a914 100644 --- a/ports/sh/Makefile +++ b/ports/sh/Makefile @@ -15,6 +15,7 @@ SRC_C = \ ports/sh/debug.c \ ports/sh/keymap.c \ ports/sh/modcasioplot.c \ + ports/sh/modkandinsky.c \ ports/sh/modgint.c \ ports/sh/mphalport.c \ ports/sh/pyexec.c \ @@ -26,6 +27,7 @@ SRC_C = \ SRC_QSTR += \ ports/sh/main.c \ ports/sh/modcasioplot.c \ + ports/sh/modkandinsky.c \ ports/sh/modgint.c \ ports/sh/pyexec.c \ diff --git a/ports/sh/modkandinsky.c b/ports/sh/modkandinsky.c new file mode 100644 index 000000000..6aac9cd91 --- /dev/null +++ b/ports/sh/modkandinsky.c @@ -0,0 +1,239 @@ +//---------------------------------------------------------------------------// +// ____ PythonExtra // +//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. // +//.-'` `---` ' License: MIT (except some files; see LICENSE) // +//---------------------------------------------------------------------------// +// pe.modkandinsky: Compatibility module for NumWorks Kandinsky library + +#include "py/obj.h" +#include "py/runtime.h" +#include +#include +#include + +#include +#include + +extern font_t numworks; + +static int callback(void) { + dupdate(); + return TIMER_CONTINUE; +} + +static mp_obj_t Kandinsky_make_color(color_t color) { + int r, g, b; + r = (color >> 8) & 0xf8; + g = (color >> 3) & 0xfc; + b = (color << 3) & 0xfc; + + mp_obj_t items[3] = { + MP_OBJ_NEW_SMALL_INT(r), + MP_OBJ_NEW_SMALL_INT(g), + MP_OBJ_NEW_SMALL_INT(b), + }; + return mp_obj_new_tuple(3, items); +} + +static mp_obj_t Kandinsky_init(void) { + void pe_enter_graphics_mode(void); + pe_enter_graphics_mode(); + + dclear(C_WHITE); + + int t = timer_configure(TIMER_TMU, 33333, GINT_CALL(callback)); + if (t >= 0) + timer_start(t); + + return mp_const_none; +} + +static mp_obj_t Kandinsky_color(size_t n, mp_obj_t const *args) { + int r = mp_obj_get_int(args[0]); + int g = mp_obj_get_int(args[1]); + int b = mp_obj_get_int(args[2]); + int color = C_RGB(r >> 3, g >> 3, b >> 3); + + return mp_obj_new_int(color); +} + +int Internal_Get_Color_From_String(const char *str) { + + if (strcmp(str, "red") == 0) + return C_RGB(31, 0, 0); + else if (strcmp(str, "r") == 0) + return C_RGB(31, 0, 0); + + else if (strcmp(str, "green") == 0) + return C_RGB(0, 31, 0); + else if (strcmp(str, "g") == 0) + return C_RGB(0, 31, 0); + + else if (strcmp(str, "blue") == 0) + return C_RGB(0, 0, 31); + else if (strcmp(str, "b") == 0) + return C_RGB(0, 0, 31); + + else if (strcmp(str, "black") == 0) + return C_RGB(0, 0, 0); + else if (strcmp(str, "k") == 0) + return C_RGB(0, 0, 0); + + else if (strcmp(str, "white") == 0) + return C_RGB(31, 31, 31); + else if (strcmp(str, "w") == 0) + return C_RGB(31, 31, 31); + + else if (strcmp(str, "yellow") == 0) + return C_RGB(31, 31, 0); + else if (strcmp(str, "y") == 0) + return C_RGB(31, 31, 0); + + // Data can be found here + // https://github.com/numworks/epsilon/blob/master/escher/include/escher/palette.h + // and here + // https://github.com/numworks/epsilon/blob/master/python/port/port.cpp#L221 + + else if (strcmp(str, "pink") == 0) + return C_RGB(0xFF >> 3, 0xAB >> 3, 0xB6 >> 3); + else if (strcmp(str, "magenta") == 0) + return C_RGB(31, 0, 31); + else if (strcmp(str, "grey") == 0) + return C_RGB(0xA7 >> 3, 0xA7 >> 3, 0xA7 >> 3); + else if (strcmp(str, "gray") == 0) + return C_RGB(0xA7 >> 3, 0xA7 >> 3, 0xA7 >> 3); + else if (strcmp(str, "purple") == 0) + return C_RGB(15, 0, 31); + else if (strcmp(str, "orange") == 0) + return C_RGB(31, 15, 0); + else if (strcmp(str, "cyan") == 0) + return C_RGB(31, 15, 0); + else + return C_RGB(0, 0, 0); +} + +int Internal_Treat_Color(mp_obj_t color) { + const mp_obj_type_t *type = mp_obj_get_type(color); + + if (type == &mp_type_str) { + size_t text_len; + char const *text = mp_obj_str_get_data(color, &text_len); + return Internal_Get_Color_From_String(text); + } + + else if (type == &mp_type_int) + return mp_obj_get_int(color); + + else if (type == &mp_type_tuple) { + size_t tuple_len; + mp_obj_t *items; + mp_obj_tuple_get(color, &tuple_len, &items); + int r = mp_obj_get_int(items[0]) >> 3; + int g = mp_obj_get_int(items[1]) >> 3; + int b = mp_obj_get_int(items[2]) >> 3; + return C_RGB(r, g, b); + } else + return C_BLACK; +} + +static mp_obj_t Kandinsky_fill_rect(size_t n, mp_obj_t const *args) { + int x = mp_obj_get_int(args[0]); + int y = mp_obj_get_int(args[1]); + int w = mp_obj_get_int(args[2]); + int h = mp_obj_get_int(args[3]); + + int color = Internal_Treat_Color(args[4]); + + drect(x, y, x + w, y + h, color); + + return mp_const_none; +} + +static mp_obj_t Kandinsky_set_pixel(size_t n, mp_obj_t const *args) { + int x = mp_obj_get_int(args[0]); + int y = mp_obj_get_int(args[1]); + + int color; + if (n == 3) + color = Internal_Treat_Color(args[2]); + else + color = C_BLACK; + + dpixel(x, y, color); + return mp_const_none; +} + +static mp_obj_t Kandinsky_get_pixel(mp_obj_t _x, mp_obj_t _y) { + int x = mp_obj_get_int(_x); + int y = mp_obj_get_int(_y); + + if (x >= 0 && x < DWIDTH && y >= 0 && y < DHEIGHT) { + color_t color = gint_vram[DWIDTH * y + x]; + return Kandinsky_make_color(color); + } + return mp_const_none; +} + +static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { + int x = mp_obj_get_int(args[1]); + int y = mp_obj_get_int(args[2]); + size_t text_len; + char const *text = mp_obj_str_get_data(args[0], &text_len); + char *text_free = NULL; + + /* If there are \n in the text, turn them into spaces */ + if (strchr(text, '\n')) { + text_free = strdup(text); + if (text_free) { + for (size_t i = 0; i < text_len; i++) + text_free[i] = (text_free[i] == '\n') ? ' ' : text_free[i]; + } + } + + color_t colortext = C_BLACK; + if (n >= 4) { + colortext = Internal_Treat_Color(args[3]); + } + + color_t colorback = C_NONE; + if (n >= 5) { + colorback = Internal_Treat_Color(args[4]); + } + + font_t const *old_font = dfont(&numworks); + dtext_opt(x, y, colortext, colorback, DTEXT_LEFT, DTEXT_TOP, + text_free ? text_free : text, text_len); + dfont(old_font); + + free(text_free); + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_init_obj, Kandinsky_init); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_set_pixel_obj, 3, 3, + Kandinsky_set_pixel); +MP_DEFINE_CONST_FUN_OBJ_2(Kandinsky_get_pixel_obj, Kandinsky_get_pixel); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_draw_string_obj, 3, 5, + Kandinsky_draw_string); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_fill_rect_obj, 5, 5, + Kandinsky_fill_rect); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_color_obj, 3, 3, Kandinsky_color); + +STATIC const mp_rom_map_elem_t kandinsky_module_globals_table[] = { + {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_kandinsky)}, + {MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&Kandinsky_init_obj)}, + {MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&Kandinsky_fill_rect_obj)}, + {MP_ROM_QSTR(MP_QSTR_color), MP_ROM_PTR(&Kandinsky_color_obj)}, + {MP_ROM_QSTR(MP_QSTR_set_pixel), MP_ROM_PTR(&Kandinsky_set_pixel_obj)}, + {MP_ROM_QSTR(MP_QSTR_get_pixel), MP_ROM_PTR(&Kandinsky_get_pixel_obj)}, + {MP_ROM_QSTR(MP_QSTR_draw_string), MP_ROM_PTR(&Kandinsky_draw_string_obj)}, +}; +STATIC MP_DEFINE_CONST_DICT(kandinsky_module_globals, + kandinsky_module_globals_table); + +const mp_obj_module_t kandinsky_module = { + .base = {&mp_type_module}, + .globals = (mp_obj_dict_t *)&kandinsky_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_kandinsky, kandinsky_module); diff --git a/ports/sh/mpconfigport.h b/ports/sh/mpconfigport.h index 31da969b3..4e84a7eba 100644 --- a/ports/sh/mpconfigport.h +++ b/ports/sh/mpconfigport.h @@ -5,106 +5,109 @@ //---------------------------------------------------------------------------// // pe.mpconfigport: MicroPython's main port configuration file -#include -#include #include "widget_shell.h" +#include +#include /* Debugging options: PythonExtra debug tools (pretty much required for any other one), MicroPython's verbose logging. */ /* PythonExtra's main debug flag */ -#define PE_DEBUG (0) -#define MICROPY_DEBUG_VERBOSE (0) +#define PE_DEBUG (0) +#define MICROPY_DEBUG_VERBOSE (0) /* Custom flag to remove DEBUG_printf in alloc/GC (very verbose) */ -#define MICROPY_DEBUG_VERBOSE_ALLOC (0) +#define MICROPY_DEBUG_VERBOSE_ALLOC (0) #if PE_DEBUG extern const struct _mp_print_t mp_debug_print; -#define MICROPY_DEBUG_PRINTER (&mp_debug_print) +#define MICROPY_DEBUG_PRINTER (&mp_debug_print) #endif /* Custom option to use relative imports. For instance when working at the fs root, 'import b' in '/folder/a.py' will import 'folder/b.py' not '/b.py'. */ -#define MICROPY_RELATIVE_FILE_IMPORTS (1) +#define MICROPY_RELATIVE_FILE_IMPORTS (1) /* General feature set selection Other options: BASIC_FEATURES, EXTRA_FEATURES, FULL_FEATURES, EVERYTHING */ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_CORE_FEATURES) /* Main features */ -#define MICROPY_ENABLE_COMPILER (1) -#define MICROPY_ENABLE_GC (1) -#define MICROPY_GC_SPLIT_HEAP (1) -#define MP_ENDIANNESS_BIG (1) -#define MICROPY_READER_POSIX (1) -#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) -#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) -#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) -#define MICROPY_REPL_EVENT_DRIVEN (1) +#define MICROPY_ENABLE_COMPILER (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_GC_SPLIT_HEAP (1) +#define MP_ENDIANNESS_BIG (1) +#define MICROPY_READER_POSIX (1) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) +#define MICROPY_REPL_EVENT_DRIVEN (1) /* Other features that we select against MICROPY_CONFIG_ROM_LEVEL */ -#define MICROPY_PY_FSTRINGS (1) /* in EXTRA_FEATURES */ -#define MICROPY_HELPER_REPL (1) /* in EXTRA_FEATURES */ -#define MICROPY_ENABLE_SOURCE_LINE (1) /* in EXTRA_FEATURES */ -#define MICROPY_PY_BUILTINS_STR_UNICODE (1) /* in EXTRA_FEATURES */ -#define MICROPY_PY_BUILTINS_HELP_MODULES (1) /* in EXTRA_FEATURES */ -#define MICROPY_KBD_EXCEPTION (1) /* in EXTRA_FEATURES */ -#define MICROPY_PY_SYS_PS1_PS2 (1) /* in EXTRA_FEATURES */ -#define MICROPY_MODULE_BUILTIN_INIT (1) /* in EXTRA_FEATURES */ -#define MICROPY_PY_ALL_SPECIAL_METHODS (1) /* in EXTRA_FEATURES */ +#define MICROPY_PY_FSTRINGS (1) /* in EXTRA_FEATURES */ +#define MICROPY_HELPER_REPL (1) /* in EXTRA_FEATURES */ +#define MICROPY_ENABLE_SOURCE_LINE (1) /* in EXTRA_FEATURES */ +#define MICROPY_PY_BUILTINS_STR_UNICODE (1) /* in EXTRA_FEATURES */ +#define MICROPY_PY_BUILTINS_HELP_MODULES (1) /* in EXTRA_FEATURES */ +#define MICROPY_KBD_EXCEPTION (1) /* in EXTRA_FEATURES */ +#define MICROPY_PY_SYS_PS1_PS2 (1) /* in EXTRA_FEATURES */ +#define MICROPY_MODULE_BUILTIN_INIT (1) /* in EXTRA_FEATURES */ +#define MICROPY_PY_ALL_SPECIAL_METHODS (1) /* in EXTRA_FEATURES */ #define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) /* in EXTRA_FEATURES */ -#define MICROPY_PY_BUILTINS_ROUND_INT (1) /* in EXTRA_FEATURES */ +#define MICROPY_PY_BUILTINS_ROUND_INT (1) /* in EXTRA_FEATURES */ // #define MICROPY_PY_SYS_STDFILES (1) /* in EXTRA_FEATURES */ -#define MICROPY_ALLOC_PATH_MAX (256) -#define MICROPY_ALLOC_PARSE_CHUNK_INIT (32) -#define MICROPY_MEM_STATS (0) -#define MICROPY_GC_ALLOC_THRESHOLD (1) -#define MICROPY_ENABLE_DOC_STRING (0) +#define MICROPY_ALLOC_PATH_MAX (256) +#define MICROPY_ALLOC_PARSE_CHUNK_INIT (32) +#define MICROPY_MEM_STATS (0) +#define MICROPY_GC_ALLOC_THRESHOLD (1) +#define MICROPY_ENABLE_DOC_STRING (0) #define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (1) -#define MICROPY_PY_BUILTINS_BYTEARRAY (1) -#define MICROPY_PY_BUILTINS_ENUMERATE (1) -#define MICROPY_PY_BUILTINS_FILTER (1) -#define MICROPY_PY_BUILTINS_FROZENSET (1) -#define MICROPY_PY_BUILTINS_HELP (1) -#define MICROPY_PY_BUILTINS_INPUT (1) -#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) -#define MICROPY_PY_BUILTINS_MIN_MAX (1) -#define MICROPY_PY_BUILTINS_PROPERTY (1) -#define MICROPY_PY_BUILTINS_REVERSED (1) -#define MICROPY_PY_BUILTINS_SET (1) -#define MICROPY_PY_BUILTINS_SLICE (1) +#define MICROPY_PY_BUILTINS_BYTEARRAY (1) +#define MICROPY_PY_BUILTINS_ENUMERATE (1) +#define MICROPY_PY_BUILTINS_FILTER (1) +#define MICROPY_PY_BUILTINS_FROZENSET (1) +#define MICROPY_PY_BUILTINS_HELP (1) +#define MICROPY_PY_BUILTINS_INPUT (1) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_BUILTINS_MIN_MAX (1) +#define MICROPY_PY_BUILTINS_PROPERTY (1) +#define MICROPY_PY_BUILTINS_REVERSED (1) +#define MICROPY_PY_BUILTINS_SET (1) +#define MICROPY_PY_BUILTINS_SLICE (1) /* Extra built-in modules */ -#define MICROPY_PY_ARRAY (1) -#define MICROPY_PY_COLLECTIONS (1) -#define MICROPY_PY_MATH (1) -#define MICROPY_PY_CMATH (1) -#define MICROPY_PY_GC (1) -#define MICROPY_PY_IO (1) -#define MICROPY_PY_STRUCT (1) -#define MICROPY_PY_RANDOM (1) -#define MICROPY_PY_RANDOM_EXTRA_FUNCS (1) -#define MICROPY_PY_SYS (1) -#define MICROPY_PY_TIME (1) +#define MICROPY_PY_ARRAY (1) +#define MICROPY_PY_COLLECTIONS (1) +#define MICROPY_PY_MATH (1) +#define MICROPY_PY_CMATH (1) +#define MICROPY_PY_GC (1) +#define MICROPY_PY_IO (1) +#define MICROPY_PY_STRUCT (1) +#define MICROPY_PY_RANDOM (1) +#define MICROPY_PY_RANDOM_EXTRA_FUNCS (1) +#define MICROPY_PY_SYS (1) +#define MICROPY_PY_TIME (1) // TODO: Enable the os module: // #define MICROPY_PY_UOS (1) // TODO: Enable other modules // #define MICROPY_PY_URE (1) // + other flags? /* Enable alias of u-modules */ -#define MICROPY_MODULE_WEAK_LINKS (1) +#define MICROPY_MODULE_WEAK_LINKS (1) /* Command executed automatically after every shell input */ -void pe_after_python_exec( - int input_kind, int exec_flags, void *ret_val, int *ret); +void pe_after_python_exec(int input_kind, int exec_flags, void *ret_val, + int *ret); #define MICROPY_BOARD_AFTER_PYTHON_EXEC pe_after_python_exec /* Command executed regularly during execution */ extern void pe_draw(void); extern widget_shell *pe_shell; -#define MICROPY_VM_HOOK_LOOP \ - { if(pe_shell->widget.update) pe_draw(); } +#define MICROPY_VM_HOOK_LOOP \ + { \ + if (pe_shell->widget.update) \ + pe_draw(); \ + } /* extra built in names to add to the global namespace #define MICROPY_PORT_BUILTINS \ @@ -116,6 +119,6 @@ typedef uintptr_t mp_uint_t; typedef long mp_off_t; #define MICROPY_HW_BOARD_NAME "sh7305" -#define MICROPY_HW_MCU_NAME "sh-4a" +#define MICROPY_HW_MCU_NAME "sh-4a" -#define MP_STATE_PORT MP_STATE_VM +#define MP_STATE_PORT MP_STATE_VM \ No newline at end of file From 2ac213cae049befded5b58ed38e37bac3f4c090d Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Mon, 29 Jan 2024 23:16:01 +0100 Subject: [PATCH 03/27] pursue NW support with dedicated modules (Ion, Time and Kandinsky now working) (only for fxCG50) --- ports/sh/Makefile | 8 +- ports/sh/numworks/ion.c | 118 +++++++++++++++++++++++++ ports/sh/numworks/ionkeyNW.h | 52 +++++++++++ ports/sh/{ => numworks}/modkandinsky.c | 0 ports/sh/numworks/time.c | 71 +++++++++++++++ 5 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 ports/sh/numworks/ion.c create mode 100644 ports/sh/numworks/ionkeyNW.h rename ports/sh/{ => numworks}/modkandinsky.c (100%) create mode 100644 ports/sh/numworks/time.c diff --git a/ports/sh/Makefile b/ports/sh/Makefile index af214a914..657d9895b 100644 --- a/ports/sh/Makefile +++ b/ports/sh/Makefile @@ -15,7 +15,9 @@ SRC_C = \ ports/sh/debug.c \ ports/sh/keymap.c \ ports/sh/modcasioplot.c \ - ports/sh/modkandinsky.c \ + ports/sh/numworks/modkandinsky.c \ + ports/sh/numworks/ion.c \ + ports/sh/numworks/time.c \ ports/sh/modgint.c \ ports/sh/mphalport.c \ ports/sh/pyexec.c \ @@ -27,7 +29,9 @@ SRC_C = \ SRC_QSTR += \ ports/sh/main.c \ ports/sh/modcasioplot.c \ - ports/sh/modkandinsky.c \ + ports/sh/numworks/modkandinsky.c \ + ports/sh/numworks/ion.c \ + ports/sh/numworks/time.c \ ports/sh/modgint.c \ ports/sh/pyexec.c \ diff --git a/ports/sh/numworks/ion.c b/ports/sh/numworks/ion.c new file mode 100644 index 000000000..f381c936d --- /dev/null +++ b/ports/sh/numworks/ion.c @@ -0,0 +1,118 @@ +//---------------------------------------------------------------------------// +// ____ PythonExtra // +//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. // +//.-'` `---` ' License: MIT (except some files; see LICENSE) // +//---------------------------------------------------------------------------// +// pe.ion: `gint` module +// +// This module aims to wrap commonly-used gint functions (not all APIs are +// considered relevant for high-level Python development). +//--- + +#include "console.h" +#include "py/objtuple.h" +#include "py/runtime.h" +#include + +#include "ionkeyNW.h" + +#include +#include + +#define FUN_0(NAME) MP_DEFINE_CONST_FUN_OBJ_0(ion_##NAME##_obj, ion_##NAME) +#define FUN_1(NAME) MP_DEFINE_CONST_FUN_OBJ_1(ion_##NAME##_obj, ion_##NAME) +#define FUN_2(NAME) MP_DEFINE_CONST_FUN_OBJ_2(ion_##NAME##_obj, ion_##NAME) +#define FUN_3(NAME) MP_DEFINE_CONST_FUN_OBJ_3(ion_##NAME##_obj, ion_##NAME) +#define FUN_VAR(NAME, MIN) \ + MP_DEFINE_CONST_FUN_OBJ_VAR(ion_##NAME##_obj, MIN, ion_##NAME) +#define FUN_BETWEEN(NAME, MIN, MAX) \ + MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ion_##NAME##_obj, MIN, MAX, ion_##NAME) + +STATIC mp_obj_t ion___init__(void) { return mp_const_none; } + +/* */ + +STATIC mp_obj_t ion_keydown(mp_obj_t arg1) { + mp_int_t key = mp_obj_get_int(arg1); + clearevents(); + bool down = keydown(key) != 0; + return mp_obj_new_bool(down); +} + +FUN_1(keydown); +FUN_0(__init__); + +/* Module definition */ + +// Helper: define object "ion_F_obj" as object "F" in the module +#define OBJ(F) \ + { MP_ROM_QSTR(MP_QSTR_##F), MP_ROM_PTR(&ion_##F##_obj) } + +// Helper: define small integer constant "I" as "I" in the module +#define INT(I) \ + { MP_ROM_QSTR(MP_QSTR_##I), MP_OBJ_NEW_SMALL_INT(I) } + +STATIC const mp_rom_map_elem_t ion_module_globals_table[] = { + {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_ion)}, + OBJ(__init__), + + /*Numworks keycodes */ + + INT(KEY_LEFT), // value 0 + INT(KEY_UP), + INT(KEY_DOWN), + INT(KEY_RIGHT), + INT(KEY_OK), + INT(KEY_BACK), + INT(KEY_HOME), + INT(KEY_ONOFF), // value 7 + + INT(KEY_SHIFT), // value 12 + INT(KEY_ALPHA), + INT(KEY_XNT), + INT(KEY_VAR), + INT(KEY_TOOLBOX), + INT(KEY_BACKSPACE), + INT(KEY_EXP), + INT(KEY_LN), + INT(KEY_LOG), + INT(KEY_IMAGINARY), + INT(KEY_COMMA), + INT(KEY_POWER), + INT(KEY_SINE), + INT(KEY_COSINE), + INT(KEY_TANGENT), + INT(KEY_PI), + INT(KEY_SQRT), + INT(KEY_SQUARE), + INT(KEY_SEVEN), + INT(KEY_EIGHT), + INT(KEY_NINE), + INT(KEY_LEFTPARENTHESIS), + INT(KEY_RIGHTPARENTHESIS), + INT(KEY_FOUR), + INT(KEY_FIVE), + INT(KEY_SIX), + INT(KEY_MULTIPLICATION), + INT(KEY_DIVISION), + INT(KEY_ONE), + INT(KEY_TWO), + INT(KEY_THREE), + INT(KEY_PLUS), + INT(KEY_MINUS), + INT(KEY_ZERO), + INT(KEY_DOT), + INT(KEY_EE), + INT(KEY_ANS), + INT(KEY_EXE), // value 52 + + OBJ(keydown), +}; +STATIC MP_DEFINE_CONST_DICT(ion_module_globals, ion_module_globals_table); + +const mp_obj_module_t ion_module = { + .base = {&mp_type_module}, + .globals = (mp_obj_dict_t *)&ion_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_ion, ion_module); diff --git a/ports/sh/numworks/ionkeyNW.h b/ports/sh/numworks/ionkeyNW.h new file mode 100644 index 000000000..1b9ae3c56 --- /dev/null +++ b/ports/sh/numworks/ionkeyNW.h @@ -0,0 +1,52 @@ +#include + +// the following table aims at providing a keymap for NW on Casio +// line that are commented correspond to keys that are similar (with exact same +// name) between NW and Casio + +//#define KEY_LEFT +//#define KEY_UP +//#define KEY_DOWN +//#define KEY_RIGHT +#define KEY_OK KEY_F1 +#define KEY_BACK KEY_EXIT +#define KEY_HOME KEY_MENU +#define KEY_ONOFF KEY_ACON +//#define KEY_SHIFT +//#define KEY_ALPHA +#define KEY_XNT KEY_XOT +#define KEY_VAR KEY_VARS +#define KEY_TOOLBOX KEY_OPTN +#define KEY_BACKSPACE KEY_DEL +//#define KEY_EXP // Note this one may be challenging +//#define KEY_LN +//#define KEY_LOG +#define KEY_IMAGINARY KEY_F2 +//#define KEY_COMMA +//#define KEY_POWER +#define KEY_SINE KEY_SIN +#define KEY_COSINE KEY_COS +#define KEY_TANGENT KEY_TAN +#define KEY_PI KEY_F3 +#define KEY_SQRT KEY_F4 +//#define KEY_SQUARE +#define KEY_SEVEN KEY_7 +#define KEY_EIGHT KEY_8 +#define KEY_NINE KEY_9 +#define KEY_LEFTPARENTHESIS KEY_LEFTP +#define KEY_RIGHTPARENTHESIS KEY_RIGHTP +#define KEY_FOUR KEY_4 +#define KEY_FIVE KEY_5 +#define KEY_SIX KEY_6 +#define KEY_MULTIPLICATION KEY_MUL +#define KEY_DIVISION KEY_DIV +#define KEY_ONE KEY_1 +#define KEY_TWO KEY_2 +#define KEY_THREE KEY_3 +#define KEY_PLUS KEY_ADD +#define KEY_MINUS KEY_SUB +#define KEY_ZERO KEY_0 +//#define KEY_DOT +#define KEY_EE KEY_F5 +#define KEY_ANS KEY_NEG +//#define KEY_EXE \ No newline at end of file diff --git a/ports/sh/modkandinsky.c b/ports/sh/numworks/modkandinsky.c similarity index 100% rename from ports/sh/modkandinsky.c rename to ports/sh/numworks/modkandinsky.c diff --git a/ports/sh/numworks/time.c b/ports/sh/numworks/time.c new file mode 100644 index 000000000..7e26c16d0 --- /dev/null +++ b/ports/sh/numworks/time.c @@ -0,0 +1,71 @@ +//---------------------------------------------------------------------------// +// ____ PythonExtra // +//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. // +//.-'` `---` ' License: MIT (except some files; see LICENSE) // +//---------------------------------------------------------------------------// +// pe.time: `gint` module +// +// This module aims to wrap commonly-used gint functtimes (not all APIs are +// considered relevant for high-level Python development). +//--- + +#include "console.h" +#include "py/objtuple.h" +#include "py/runtime.h" +#include + +#include +#include +#include + +#define FUN_0(NAME) MP_DEFINE_CONST_FUN_OBJ_0(time_##NAME##_obj, time_##NAME) +#define FUN_1(NAME) MP_DEFINE_CONST_FUN_OBJ_1(time_##NAME##_obj, time_##NAME) +#define FUN_2(NAME) MP_DEFINE_CONST_FUN_OBJ_2(time_##NAME##_obj, time_##NAME) +#define FUN_3(NAME) MP_DEFINE_CONST_FUN_OBJ_3(time_##NAME##_obj, time_##NAME) +#define FUN_VAR(NAME, MIN) \ + MP_DEFINE_CONST_FUN_OBJ_VAR(time_##NAME##_obj, MIN, time_##NAME) +#define FUN_BETWEEN(NAME, MIN, MAX) \ + MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(time_##NAME##_obj, MIN, MAX, time_##NAME) + +STATIC mp_obj_t time___init__(void) { return mp_const_none; } + +/* */ + +STATIC mp_obj_t time_sleep(mp_obj_t arg1) { + mp_float_t duration = mp_obj_get_float(arg1); + + uint64_t length = + (uint64_t)(duration * + 1000000.0f); // duration is in seconds and length in µs + + sleep_us(length); + + return mp_const_none; +} + +FUN_1(sleep); +FUN_0(__init__); + +/* Module definittime */ + +// Helper: define object "time_F_obj" as object "F" in the module +#define OBJ(F) \ + { MP_ROM_QSTR(MP_QSTR_##F), MP_ROM_PTR(&time_##F##_obj) } + +// Helper: define small integer constant "I" as "I" in the module +#define INT(I) \ + { MP_ROM_QSTR(MP_QSTR_##I), MP_OBJ_NEW_SMALL_INT(I) } + +STATIC const mp_rom_map_elem_t time_module_globals_table[] = { + {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_time)}, + OBJ(__init__), + OBJ(sleep), +}; +STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); + +const mp_obj_module_t time_module = { + .base = {&mp_type_module}, + .globals = (mp_obj_dict_t *)&time_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_time, time_module); From 0868d96d2ba9a84f17594acc9248b84ed49cdf2e Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Tue, 30 Jan 2024 18:49:17 +0100 Subject: [PATCH 04/27] WIP NW module on Casio - Restoration of correct dwindow and timers at module exit --- ports/sh/main.c | 659 ++++++++++++++++--------------- ports/sh/numworks/colorsNW.h | 21 + ports/sh/numworks/modkandinsky.c | 95 +++-- 3 files changed, 399 insertions(+), 376 deletions(-) create mode 100644 ports/sh/numworks/colorsNW.h diff --git a/ports/sh/main.c b/ports/sh/main.c index eccf9d4ab..58f490ff0 100644 --- a/ports/sh/main.c +++ b/ports/sh/main.c @@ -4,66 +4,72 @@ //.-'` `---` ' License: MIT (except some files; see LICENSE) // //---------------------------------------------------------------------------// +#include "py/builtin.h" #include "py/compile.h" #include "py/gc.h" -#include "py/stackctrl.h" -#include "py/builtin.h" #include "py/mphal.h" #include "py/repl.h" -#include "shared/runtime/gchelper.h" +#include "py/stackctrl.h" #include "pyexec.h" +#include "shared/runtime/gchelper.h" #include #include +#include #include #include -#include +#include -#include -#include -#include #include +#include +#include #include +#include -#include -#include #include #include #include +#include +#include -#include "mpconfigport.h" #include "console.h" -#include "widget_shell.h" #include "debug.h" +#include "mpconfigport.h" +#include "widget_shell.h" //=== Application globals ===// struct pe_globals { - /* The terminal data structure (with lines, editing functions, etc). */ - console_t *console; - /* The global JustUI scene. */ - jscene *scene; - /* The widget shell (which connects the GUI to `console`). */ - widget_shell *shell; - /* The file selection widget. */ - jfileselect *fileselect; - /* Title widget and whether to show it in the shell. */ - jlabel *title; - bool show_title_in_shell; + /* The terminal data structure (with lines, editing functions, etc). */ + console_t *console; + /* The global JustUI scene. */ + jscene *scene; + /* The widget shell (which connects the GUI to `console`). */ + widget_shell *shell; + /* The file selection widget. */ + jfileselect *fileselect; + /* Title widget and whether to show it in the shell. */ + jlabel *title; + bool show_title_in_shell; }; // TODO: Put pe_globals in a header for use by the loop hook in mpconfigport.h widget_shell *pe_shell; -struct pe_globals PE = { 0 }; +struct pe_globals PE = {0}; + +// TODO : make this more clean by putting these globals into pe_globals and +// making this accessible to modules +bool is_dwindowed = false; +bool is_timered = false; +unsigned int timer_altered[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; //=== Hook for redirecting stdout/stderr to the shell ===// -static ssize_t stdouterr_write(void *data, void const *buf, size_t size) -{ - console_t *cons = data; - console_write(cons, buf, size); - return size; +static ssize_t stdouterr_write(void *data, void const *buf, size_t size) { + console_t *cons = data; + console_write(cons, buf, size); + return size; } fs_descriptor_type_t stdouterr_type = { @@ -75,72 +81,67 @@ fs_descriptor_type_t stdouterr_type = { //=== File filter for the selection dialog ===// -static bool strendswith(char const *str, char const *suffix) -{ - size_t l1 = strlen(str); - size_t l2 = strlen(suffix); +static bool strendswith(char const *str, char const *suffix) { + size_t l1 = strlen(str); + size_t l2 = strlen(suffix); - return l1 >= l2 && strcmp(str + l1 - l2, suffix) == 0; + return l1 >= l2 && strcmp(str + l1 - l2, suffix) == 0; } -static bool py_file_filter(struct dirent const *ent) -{ - if(!jfileselect_default_filter(ent)) - return false; - if(ent->d_type == DT_DIR) - return true; - return strendswith(ent->d_name, ".py"); +static bool py_file_filter(struct dirent const *ent) { + if (!jfileselect_default_filter(ent)) + return false; + if (ent->d_type == DT_DIR) + return true; + return strendswith(ent->d_name, ".py"); } //=== Module loading utilities ===// -static char *path_to_module(char const *path) -{ - if(path[0] == '/') - path++; +static char *path_to_module(char const *path) { + if (path[0] == '/') + path++; - int i, n = strlen(path); - char *module = malloc(n + 1); - if(!module) - return NULL; + int i, n = strlen(path); + char *module = malloc(n + 1); + if (!module) + return NULL; - for(i = 0; i < n; i++) { - if(i == n - 3 && !strcmp(path + i, ".py")) - break; - module[i] = (path[i] == '/') ? '.' : path[i]; - } - module[i] = 0; - return module; + for (i = 0; i < n; i++) { + if (i == n - 3 && !strcmp(path + i, ".py")) + break; + module[i] = (path[i] == '/') ? '.' : path[i]; + } + module[i] = 0; + return module; } //=== AC/ON interrupt mechanism ===// /* Filter AC/ON push events asynchronously from the keyboard driver and interrupt MicroPython instead. */ -static bool async_filter(key_event_t ev) -{ - /* Gobble all events related to AC/ON to make sure that the keyboard driver - treats them as handled. Otherwise, we'd run the risk of filling the - event queue (if the user doesn't read from it) thus preventing the - driver from handling AC/ON releases, which disables further presses. */ - if(mp_interrupt_char >= 0 && ev.key == KEY_ACON) { - /* This function supports asynchronous calls, by design. */ - if(ev.type == KEYEV_DOWN) - mp_sched_keyboard_interrupt(); - return false; - } +static bool async_filter(key_event_t ev) { + /* Gobble all events related to AC/ON to make sure that the keyboard driver + treats them as handled. Otherwise, we'd run the risk of filling the + event queue (if the user doesn't read from it) thus preventing the + driver from handling AC/ON releases, which disables further presses. */ + if (mp_interrupt_char >= 0 && ev.key == KEY_ACON) { + /* This function supports asynchronous calls, by design. */ + if (ev.type == KEYEV_DOWN) + mp_sched_keyboard_interrupt(); + return false; + } - return true; + return true; } void pe_after_python_exec(int input_kind, int exec_flags, void *ret_val, - int *ret) -{ - (void)input_kind; - (void)exec_flags; - (void)ret_val; - (void)ret; - clearevents(); + int *ret) { + (void)input_kind; + (void)exec_flags; + (void)ret_val; + (void)ret; + clearevents(); } //=== Rendering ===// @@ -155,335 +156,343 @@ extern bopti_image_t const img_modifier_states; #define _(fx, cg) (cg) #endif -void pe_enter_graphics_mode(void) -{ - /* Cancel any pending update of the shell */ - PE.console->render_needed = false; - PE.shell->widget.update = 0; +void pe_enter_graphics_mode(void) { + /* Cancel any pending update of the shell */ + PE.console->render_needed = false; + PE.shell->widget.update = 0; } -void pe_draw(void) -{ - dclear(C_WHITE); - jscene_render(PE.scene); +void pe_draw(void) { + dclear(C_WHITE); + jscene_render(PE.scene); - /* Render shell modifiers above the scene in a convenient spot */ - int shift, alpha, layer; - bool instant; - widget_shell_get_modifiers(PE.shell, &shift, &alpha); - widget_shell_modifier_info(shift, alpha, &layer, &instant); - int icon = 2 * layer + !instant; + /* Render shell modifiers above the scene in a convenient spot */ + int shift, alpha, layer; + bool instant; + widget_shell_get_modifiers(PE.shell, &shift, &alpha); + widget_shell_modifier_info(shift, alpha, &layer, &instant); + int icon = 2 * layer + !instant; #ifdef FX9860G - dsubimage(118, 58, &img_modifier_states, 9*icon+1, 1, 8, 6, - DIMAGE_NONE); + dsubimage(118, 58, &img_modifier_states, 9 * icon + 1, 1, 8, 6, DIMAGE_NONE); #else - dsubimage(377, 207, &img_modifier_states, 16*icon, 0, 15, 14, - DIMAGE_NONE); + dsubimage(377, 207, &img_modifier_states, 16 * icon, 0, 15, 14, DIMAGE_NONE); #endif - dupdate(); + dupdate(); } //=== Application control functions ===// -static void pe_reset_micropython(void) -{ - gc_sweep_all(); - mp_deinit(); - mp_init(); +void pe_restore_window_and_timer(void) { + if (is_dwindowed) { + struct dwindow win; + win.left = 0; + win.top = 0; + win.right = DWIDTH; + win.bottom = DHEIGHT; + dwindow_set(win); + is_dwindowed = false; // we mark as not windowed + } -#ifdef FX9860G - char const *msg = "**SHELL INIT.**\n"; -#else - char const *msg = "*** SHELL INITIALIZED ***\n"; -#endif - - console_newline(PE.console); - console_write(PE.console, msg, -1); - pyexec_event_repl_init(); + if (is_timered) { + for (int u = 0; u < 9; u++) + if (timer_altered[u] == 1) + timer_stop(u); + is_timered = false; + } } -static void pe_print_prompt(int which) -{ - char const *prompt = NULL; - if(which == 2) - prompt = mp_repl_get_ps2(); - else - prompt = mp_repl_get_ps1(); +static void pe_reset_micropython(void) { + gc_sweep_all(); + mp_deinit(); + mp_init(); - console_write(PE.console, prompt, -1); - console_lock_prefix(PE.console); +#ifdef FX9860G + char const *msg = "**SHELL INIT.**\n"; +#else + char const *msg = "*** SHELL INITIALIZED ***\n"; +#endif + + console_newline(PE.console); + console_write(PE.console, msg, -1); + pyexec_event_repl_init(); +} + +static void pe_print_prompt(int which) { + char const *prompt = NULL; + if (which == 2) + prompt = mp_repl_get_ps2(); + else + prompt = mp_repl_get_ps1(); + + console_write(PE.console, prompt, -1); + console_lock_prefix(PE.console); } /* Handle a GUI event. If `shell_bound` is true, only actions that have an effect on the shell are allowed and the return value is any full line that is entered in the shell. Otherwise, the full GUI is available and the return value is NULL. */ -static char *pe_handle_event(jevent e, bool shell_bound) -{ - if(e.type == JSCENE_PAINT) - pe_draw(); +static char *pe_handle_event(jevent e, bool shell_bound) { + if (e.type == JSCENE_PAINT) + pe_draw(); - if(e.type == WIDGET_SHELL_MOD_CHANGED) - PE.scene->widget.update = true; + if (e.type == WIDGET_SHELL_MOD_CHANGED) + PE.scene->widget.update = true; - if(e.type == WIDGET_SHELL_INPUT) { - char *line = (char *)e.data; - if(shell_bound) { - return line; - } - else { - pyexec_repl_execute(line); - free(line); - pe_print_prompt(1); - } + if (e.type == WIDGET_SHELL_INPUT) { + char *line = (char *)e.data; + if (shell_bound) { + return line; + } else { + pyexec_repl_execute(line); + free(line); + pe_print_prompt(1); } + } - if(!shell_bound && e.type == JFILESELECT_VALIDATED) { - char const *path = jfileselect_selected_file(PE.fileselect); - char *module = path_to_module(path); - if(module) { - jscene_show_and_focus(PE.scene, PE.shell); - jwidget_set_visible(PE.title, PE.show_title_in_shell); + if (!shell_bound && e.type == JFILESELECT_VALIDATED) { + char const *path = jfileselect_selected_file(PE.fileselect); + char *module = path_to_module(path); + if (module) { + jscene_show_and_focus(PE.scene, PE.shell); + jwidget_set_visible(PE.title, PE.show_title_in_shell); - pe_reset_micropython(); + pe_reset_micropython(); - char *str = malloc(8 + strlen(module) + 1); - if(str) { - strcpy(str, "import "); - strcat(str, module); - pyexec_repl_execute(str); - free(str); - } - free(module); + char *str = malloc(8 + strlen(module) + 1); + if (str) { + strcpy(str, "import "); + strcat(str, module); + pyexec_repl_execute(str); + free(str); + } + free(module); - pe_print_prompt(1); - } - } - - if(e.type != JWIDGET_KEY || e.key.type == KEYEV_UP) - return NULL; - int key = e.key.key; - - if(key == KEY_SQUARE && !e.key.shift && e.key.alpha) - pe_debug_screenshot(); - if(key == KEY_TAN) - pe_debug_kmalloc(); - - if(!shell_bound && key == KEY_F1) { - jscene_show_and_focus(PE.scene, PE.fileselect); - jwidget_set_visible(PE.title, true); - } - if(!shell_bound && key == KEY_F2) { - jscene_show_and_focus(PE.scene, PE.shell); - jwidget_set_visible(PE.title, PE.show_title_in_shell); + pe_restore_window_and_timer(); + + pe_print_prompt(1); } + } + if (e.type != JWIDGET_KEY || e.key.type == KEYEV_UP) return NULL; -} + int key = e.key.key; -int pe_readline(vstr_t *line, char const *prompt) -{ - console_write(PE.console, prompt, -1); - console_lock_prefix(PE.console); - - int c = mp_interrupt_char; - mp_hal_set_interrupt_char(-1); - - char *text = NULL; - while(!text) { - jevent e = jscene_run(PE.scene); - text = pe_handle_event(e, true); - } - - mp_hal_set_interrupt_char(c); - vstr_reset(line); - vstr_add_str(line, text); - free(text); - return 0; // TODO: return CHAR_CTRL_C on AC/ON instead -} - -int main(int argc, char **argv) -{ - pe_debug_init(); + if (key == KEY_SQUARE && !e.key.shift && e.key.alpha) + pe_debug_screenshot(); + if (key == KEY_TAN) pe_debug_kmalloc(); - //=== Init sequence ===// + if (!shell_bound && key == KEY_F1) { + jscene_show_and_focus(PE.scene, PE.fileselect); + jwidget_set_visible(PE.title, true); + } + if (!shell_bound && key == KEY_F2) { + jscene_show_and_focus(PE.scene, PE.shell); + jwidget_set_visible(PE.title, PE.show_title_in_shell); + } - keydev_set_async_filter(keydev_std(), async_filter); + return NULL; +} - /* Make delayed shift/alpha permanent so the stay between calls to - jscene_run() and can be used for delayed key combos */ - keydev_transform_t tr = keydev_transform(keydev_std()); - tr.enabled |= KEYDEV_TR_DELAYED_SHIFT; - tr.enabled |= KEYDEV_TR_DELAYED_ALPHA; - keydev_set_transform(keydev_std(), tr); +int pe_readline(vstr_t *line, char const *prompt) { + console_write(PE.console, prompt, -1); + console_lock_prefix(PE.console); - PE.console = console_create(8192, 200); + int c = mp_interrupt_char; + mp_hal_set_interrupt_char(-1); - /* Set up standard streams */ - close(STDOUT_FILENO); - close(STDERR_FILENO); - open_generic(&stdouterr_type, PE.console, STDOUT_FILENO); - open_generic(&stdouterr_type, PE.console, STDERR_FILENO); + char *text = NULL; + while (!text) { + jevent e = jscene_run(PE.scene); + text = pe_handle_event(e, true); + } - /* Initialize the MicroPython GC with most available memory */ - mp_stack_ctrl_init(); + mp_hal_set_interrupt_char(c); + vstr_reset(line); + vstr_add_str(line, text); + free(text); + return 0; // TODO: return CHAR_CTRL_C on AC/ON instead +} + +int main(int argc, char **argv) { + pe_debug_init(); + pe_debug_kmalloc(); + + //=== Init sequence ===// + + keydev_set_async_filter(keydev_std(), async_filter); + + /* Make delayed shift/alpha permanent so the stay between calls to + jscene_run() and can be used for delayed key combos */ + keydev_transform_t tr = keydev_transform(keydev_std()); + tr.enabled |= KEYDEV_TR_DELAYED_SHIFT; + tr.enabled |= KEYDEV_TR_DELAYED_ALPHA; + keydev_set_transform(keydev_std(), tr); + + PE.console = console_create(8192, 200); + + /* Set up standard streams */ + close(STDOUT_FILENO); + close(STDERR_FILENO); + open_generic(&stdouterr_type, PE.console, STDOUT_FILENO); + open_generic(&stdouterr_type, PE.console, STDERR_FILENO); + + /* Initialize the MicroPython GC with most available memory */ + mp_stack_ctrl_init(); #ifdef FX9860G - /* Put basically all the OS heap into the Python GC. For our own purposes - we'll use gint's _uram arena and the leftover from the OS heap. */ - int size = 65536; - bool first = true; + /* Put basically all the OS heap into the Python GC. For our own purposes + we'll use gint's _uram arena and the leftover from the OS heap. */ + int size = 65536; + bool first = true; - while(size >= 2048) { - void *area = kmalloc(size, "_os"); - if(area) { - if(first) - gc_init(area, area + size); - else - gc_add(area, area + size); - first = false; - } - else size /= 2; - } + while (size >= 2048) { + void *area = kmalloc(size, "_os"); + if (area) { + if (first) + gc_init(area, area + size); + else + gc_add(area, area + size); + first = false; + } else + size /= 2; + } - if(first) - pe_debug_panic("No heap!"); + if (first) + pe_debug_panic("No heap!"); #if PE_DEBUG - /* Add some Python ram */ - // https://www.planet-casio.com/Fr/forums/topic15269-10-khicas-add-in-calcul-formel-pour-graph-90e-et-35eii.html#189284 - void *py_ram_start = (void*)0x88053800; - void *py_ram_end = (void*)0x8807f000; - gc_add(py_ram_start, py_ram_end); + /* Add some Python ram */ + // https://www.planet-casio.com/Fr/forums/topic15269-10-khicas-add-in-calcul-formel-pour-graph-90e-et-35eii.html#189284 + void *py_ram_start = (void *)0x88053800; + void *py_ram_end = (void *)0x8807f000; + gc_add(py_ram_start, py_ram_end); #endif #else - /* Get everything from the OS stack (~ 350 ko) */ - size_t gc_area_size; - void *gc_area = kmalloc_max(&gc_area_size, "_ostk"); - gc_init(gc_area, gc_area + gc_area_size); + /* Get everything from the OS stack (~ 350 ko) */ + size_t gc_area_size; + void *gc_area = kmalloc_max(&gc_area_size, "_ostk"); + gc_init(gc_area, gc_area + gc_area_size); - /* Other options: - - All of _uram (leaving the OS heap for the shell/GUI/etc) - - The OS' extra VRAM - - Memory past the 2 MB boundary on tested OSes */ - // gc_add(start, end)... + /* Other options: + - All of _uram (leaving the OS heap for the shell/GUI/etc) + - The OS' extra VRAM + - Memory past the 2 MB boundary on tested OSes */ + // gc_add(start, end)... #endif - mp_init(); + mp_init(); - /* TODO: Add an option for the shorter prompt */ + /* TODO: Add an option for the shorter prompt */ #ifdef FX9860G - MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS1]) = - MP_OBJ_NEW_QSTR(qstr_from_str(">")); - MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS2]) = - MP_OBJ_NEW_QSTR(qstr_from_str(".")); + MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS1]) = + MP_OBJ_NEW_QSTR(qstr_from_str(">")); + MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS2]) = + MP_OBJ_NEW_QSTR(qstr_from_str(".")); #endif - pyexec_event_repl_init(); - pe_print_prompt(1); + pyexec_event_repl_init(); + pe_print_prompt(1); - //=== GUI setup ===// + //=== GUI setup ===// - PE.scene = jscene_create_fullscreen(NULL); - PE.title = jlabel_create("PythonExtra", PE.scene); - jwidget *stack = jwidget_create(PE.scene); - jfkeys *fkeys = jfkeys_create2(&img_fkeys_main, "/FILES;/SHELL", PE.scene); - (void)fkeys; + PE.scene = jscene_create_fullscreen(NULL); + PE.title = jlabel_create("PythonExtra", PE.scene); + jwidget *stack = jwidget_create(PE.scene); + jfkeys *fkeys = jfkeys_create2(&img_fkeys_main, "/FILES;/SHELL", PE.scene); + (void)fkeys; - jwidget_set_stretch(PE.title, 1, 0, false); + jwidget_set_stretch(PE.title, 1, 0, false); - jlayout_set_vbox(PE.scene)->spacing = _(0, 3); - jlayout_set_stack(stack); - jwidget_set_stretch(stack, 1, 1, false); + jlayout_set_vbox(PE.scene)->spacing = _(0, 3); + jlayout_set_stack(stack); + jwidget_set_stretch(stack, 1, 1, false); - /* Filesystem tab */ - PE.fileselect = jfileselect_create(stack); - jfileselect_set_filter(PE.fileselect, py_file_filter); - jfileselect_set_show_file_size(PE.fileselect, true); - jwidget_set_stretch(PE.fileselect, 1, 1, false); + /* Filesystem tab */ + PE.fileselect = jfileselect_create(stack); + jfileselect_set_filter(PE.fileselect, py_file_filter); + jfileselect_set_show_file_size(PE.fileselect, true); + jwidget_set_stretch(PE.fileselect, 1, 1, false); - /* Shell tab */ - PE.shell = pe_shell = widget_shell_create(PE.console, stack); - widget_shell_set_line_spacing(PE.shell, _(1, 3)); - jwidget_set_stretch(PE.shell, 1, 1, false); + /* Shell tab */ + PE.shell = pe_shell = widget_shell_create(PE.console, stack); + widget_shell_set_line_spacing(PE.shell, _(1, 3)); + jwidget_set_stretch(PE.shell, 1, 1, false); #ifdef FX9860G - PE.show_title_in_shell = false; - jwidget_set_padding(PE.title, 0, 0, 1, 0); - widget_shell_set_font(PE.shell, &font_4x6); + PE.show_title_in_shell = false; + jwidget_set_padding(PE.title, 0, 0, 1, 0); + widget_shell_set_font(PE.shell, &font_4x6); #else - PE.show_title_in_shell = true; - jwidget_set_background(PE.title, C_BLACK); - jlabel_set_text_color(PE.title, C_WHITE); - jwidget_set_padding(PE.title, 3, 6, 3, 6); - jwidget_set_padding(stack, 0, 6, 0, 6); + PE.show_title_in_shell = true; + jwidget_set_background(PE.title, C_BLACK); + jlabel_set_text_color(PE.title, C_WHITE); + jwidget_set_padding(PE.title, 3, 6, 3, 6); + jwidget_set_padding(stack, 0, 6, 0, 6); #endif - /* Initial state */ - jfileselect_browse(PE.fileselect, "/"); - jscene_show_and_focus(PE.scene, PE.fileselect); + /* Initial state */ + jfileselect_browse(PE.fileselect, "/"); + jscene_show_and_focus(PE.scene, PE.fileselect); - //=== Event handling ===// + //=== Event handling ===// - while(1) { - jevent e = jscene_run(PE.scene); - pe_handle_event(e, false); - } + while (1) { + jevent e = jscene_run(PE.scene); + pe_handle_event(e, false); + } - //=== Deinitialization ===// + //=== Deinitialization ===// - gc_sweep_all(); - mp_deinit(); - console_destroy(PE.console); - return 0; + gc_sweep_all(); + mp_deinit(); + console_destroy(PE.console); + return 0; } /* Handle uncaught exceptions (normally unreachable). */ -void nlr_jump_fail(void *val) -{ - dclear(C_BLACK); - dtext(2, 2, C_WHITE, "nlr_jump_fail!"); - dprint(2, 2, C_WHITE, "val = %p", val); - dupdate(); - while(1) - getkey(); +void nlr_jump_fail(void *val) { + dclear(C_BLACK); + dtext(2, 2, C_WHITE, "nlr_jump_fail!"); + dprint(2, 2, C_WHITE, "val = %p", val); + dupdate(); + while (1) + getkey(); } /* Do a garbage collection cycle. */ -void gc_collect(void) -{ - gc_collect_start(); - gc_helper_collect_regs_and_stack(); - gc_collect_end(); +void gc_collect(void) { + gc_collect_start(); + gc_helper_collect_regs_and_stack(); + gc_collect_end(); } -mp_import_stat_t mp_import_stat(const char *path) -{ - struct stat st; - int rc = stat(path, &st); - if(rc < 0) - return MP_IMPORT_STAT_NO_EXIST; +mp_import_stat_t mp_import_stat(const char *path) { + struct stat st; + int rc = stat(path, &st); + if (rc < 0) + return MP_IMPORT_STAT_NO_EXIST; - if(S_ISDIR(st.st_mode)) - return MP_IMPORT_STAT_DIR; - else - return MP_IMPORT_STAT_FILE; + if (S_ISDIR(st.st_mode)) + return MP_IMPORT_STAT_DIR; + else + return MP_IMPORT_STAT_FILE; } // TODO: See branch 'posix-open' for a relevant attempt at using the POSIX API -mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) -{ - mp_obj_t *args_items; - size_t len; - mp_obj_get_array(*args, &len, &args_items); - printf("%d %p\n", (int)len, args_items); - if(len != 2) - return mp_const_none; - - char const *path = mp_obj_str_get_str(args_items[0]); - char const *mode = mp_obj_str_get_str(args_items[1]); - printf("'%s' '%s'\n", path, mode); - +mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, + mp_map_t *kwargs) { + mp_obj_t *args_items; + size_t len; + mp_obj_get_array(*args, &len, &args_items); + printf("%d %p\n", (int)len, args_items); + if (len != 2) return mp_const_none; + + char const *path = mp_obj_str_get_str(args_items[0]); + char const *mode = mp_obj_str_get_str(args_items[1]); + printf("'%s' '%s'\n", path, mode); + + return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); diff --git a/ports/sh/numworks/colorsNW.h b/ports/sh/numworks/colorsNW.h new file mode 100644 index 000000000..bb8161292 --- /dev/null +++ b/ports/sh/numworks/colorsNW.h @@ -0,0 +1,21 @@ +// Data can be found here +// https://github.com/numworks/epsilon/blob/master/escher/include/escher/palette.h +// and here +// https://github.com/numworks/epsilon/blob/master/python/port/port.cpp#L221 + +#define NW_RGB(r, g, b) (((r >> 3) << 11) | ((g >> 2) << 6) | (b >> 3)) + +#define NW_BLUE NW_RGB(0x50, 0x75, 0xF2) +#define NW_RED NW_RGB(0xFF, 0x00, 0x0C) +#define NW_GREEN NW_RGB(0x50, 0xC1, 0x02) +#define NW_WHITE NW_RGB(0xF7, 0xF9, 0xFA) +#define NW_BLACK NW_RGB(0x00, 0x00, 0x00) + +#define NW_YELLOW NW_RGB(0xFF, 0xCC, 0x7B) +#define NW_PURPLE NW_RGB(0x6E, 0x2D, 0x79) +#define NW_BROWN NW_RGB(0x8D, 0x73, 0x50) +#define NW_CYAN NW_RGB(0x00, 0xFF, 0xFF) +#define NW_ORANGE NW_RGB(0xFE, 0x87, 0x1F) +#define NW_PINK NW_RGB(0xFF, 0xAB, 0xB6) +#define NW_MAGENTA NW_RGB(0xFF, 0x05, 0x88) +#define NW_GRAY NW_RGB(0xA7, 0xA7, 0xA7) diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index 6aac9cd91..e6c95921f 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -14,8 +14,14 @@ #include #include +#include "colorsNW.h" + extern font_t numworks; +extern bool is_dwindowed; +extern bool is_timered; +extern unsigned int timer_altered[9]; + static int callback(void) { dupdate(); return TIMER_CONTINUE; @@ -38,13 +44,22 @@ static mp_obj_t Kandinsky_make_color(color_t color) { static mp_obj_t Kandinsky_init(void) { void pe_enter_graphics_mode(void); pe_enter_graphics_mode(); + dclear(NW_WHITE); - dclear(C_WHITE); + struct dwindow nw; + nw.left = 0; + nw.top = 0; + nw.right = 320; + nw.bottom = 240; + dwindow_set(nw); + is_dwindowed = true; // we mark as windowed int t = timer_configure(TIMER_TMU, 33333, GINT_CALL(callback)); - if (t >= 0) + if (t >= 0) { timer_start(t); - + is_timered = true; // there is a timer altered from this module + timer_altered[t] = 1; // we put the corresponding timer at 1 to identify it + } return mp_const_none; } @@ -59,57 +74,35 @@ static mp_obj_t Kandinsky_color(size_t n, mp_obj_t const *args) { int Internal_Get_Color_From_String(const char *str) { - if (strcmp(str, "red") == 0) - return C_RGB(31, 0, 0); - else if (strcmp(str, "r") == 0) - return C_RGB(31, 0, 0); - - else if (strcmp(str, "green") == 0) - return C_RGB(0, 31, 0); - else if (strcmp(str, "g") == 0) - return C_RGB(0, 31, 0); - - else if (strcmp(str, "blue") == 0) - return C_RGB(0, 0, 31); - else if (strcmp(str, "b") == 0) - return C_RGB(0, 0, 31); - - else if (strcmp(str, "black") == 0) - return C_RGB(0, 0, 0); - else if (strcmp(str, "k") == 0) - return C_RGB(0, 0, 0); - - else if (strcmp(str, "white") == 0) - return C_RGB(31, 31, 31); - else if (strcmp(str, "w") == 0) - return C_RGB(31, 31, 31); - - else if (strcmp(str, "yellow") == 0) - return C_RGB(31, 31, 0); - else if (strcmp(str, "y") == 0) - return C_RGB(31, 31, 0); - - // Data can be found here - // https://github.com/numworks/epsilon/blob/master/escher/include/escher/palette.h - // and here - // https://github.com/numworks/epsilon/blob/master/python/port/port.cpp#L221 + if (strcmp(str, "red") == 0 || strcmp(str, "r") == 0) + return NW_RED; + else if (strcmp(str, "green") == 0 || strcmp(str, "g") == 0) + return NW_GREEN; + else if (strcmp(str, "blue") == 0 || strcmp(str, "b") == 0) + return NW_BLUE; + else if (strcmp(str, "black") == 0 || strcmp(str, "k") == 0) + return NW_BLACK; + else if (strcmp(str, "white") == 0 || strcmp(str, "w") == 0) + return NW_WHITE; + else if (strcmp(str, "yellow") == 0 || strcmp(str, "y") == 0) + return NW_YELLOW; else if (strcmp(str, "pink") == 0) - return C_RGB(0xFF >> 3, 0xAB >> 3, 0xB6 >> 3); + return NW_PINK; else if (strcmp(str, "magenta") == 0) - return C_RGB(31, 0, 31); - else if (strcmp(str, "grey") == 0) - return C_RGB(0xA7 >> 3, 0xA7 >> 3, 0xA7 >> 3); - else if (strcmp(str, "gray") == 0) - return C_RGB(0xA7 >> 3, 0xA7 >> 3, 0xA7 >> 3); + return NW_MAGENTA; + else if (strcmp(str, "grey") == 0 || strcmp(str, "gray") == 0) + return NW_GRAY; else if (strcmp(str, "purple") == 0) - return C_RGB(15, 0, 31); + return NW_PURPLE; else if (strcmp(str, "orange") == 0) - return C_RGB(31, 15, 0); + return NW_ORANGE; else if (strcmp(str, "cyan") == 0) - return C_RGB(31, 15, 0); + return NW_CYAN; + else if (strcmp(str, "brown") == 0) + return NW_BROWN; else - return C_RGB(0, 0, 0); + return NW_BLACK; } int Internal_Treat_Color(mp_obj_t color) { @@ -131,9 +124,9 @@ int Internal_Treat_Color(mp_obj_t color) { int r = mp_obj_get_int(items[0]) >> 3; int g = mp_obj_get_int(items[1]) >> 3; int b = mp_obj_get_int(items[2]) >> 3; - return C_RGB(r, g, b); + return NW_RGB(r, g, b); } else - return C_BLACK; + return NW_BLACK; } static mp_obj_t Kandinsky_fill_rect(size_t n, mp_obj_t const *args) { @@ -157,7 +150,7 @@ static mp_obj_t Kandinsky_set_pixel(size_t n, mp_obj_t const *args) { if (n == 3) color = Internal_Treat_Color(args[2]); else - color = C_BLACK; + color = NW_BLACK; dpixel(x, y, color); return mp_const_none; @@ -190,7 +183,7 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { } } - color_t colortext = C_BLACK; + color_t colortext = NW_BLACK; if (n >= 4) { colortext = Internal_Treat_Color(args[3]); } From bca7048525750f52d8478d61648db32b3c96c723 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Tue, 30 Jan 2024 20:36:31 +0100 Subject: [PATCH 05/27] WIP : NW modules for fxCG50 - corrected Kandinsky Draw_String when string contains \n char --- ports/fxcg50/fxconv-metadata.txt | 2 +- ports/sh/numworks/colorsNW.h | 21 -------- ports/sh/numworks/modkandinsky.c | 93 +++++++++++++++++++++++--------- 3 files changed, 70 insertions(+), 46 deletions(-) delete mode 100644 ports/sh/numworks/colorsNW.h diff --git a/ports/fxcg50/fxconv-metadata.txt b/ports/fxcg50/fxconv-metadata.txt index 67e505288..c752d6b51 100644 --- a/ports/fxcg50/fxconv-metadata.txt +++ b/ports/fxcg50/fxconv-metadata.txt @@ -37,4 +37,4 @@ PoliceNW.png: grid.size: 10x16 grid.padding: 0 grid.border: 0 - proportional: false \ No newline at end of file + proportional: true \ No newline at end of file diff --git a/ports/sh/numworks/colorsNW.h b/ports/sh/numworks/colorsNW.h deleted file mode 100644 index bb8161292..000000000 --- a/ports/sh/numworks/colorsNW.h +++ /dev/null @@ -1,21 +0,0 @@ -// Data can be found here -// https://github.com/numworks/epsilon/blob/master/escher/include/escher/palette.h -// and here -// https://github.com/numworks/epsilon/blob/master/python/port/port.cpp#L221 - -#define NW_RGB(r, g, b) (((r >> 3) << 11) | ((g >> 2) << 6) | (b >> 3)) - -#define NW_BLUE NW_RGB(0x50, 0x75, 0xF2) -#define NW_RED NW_RGB(0xFF, 0x00, 0x0C) -#define NW_GREEN NW_RGB(0x50, 0xC1, 0x02) -#define NW_WHITE NW_RGB(0xF7, 0xF9, 0xFA) -#define NW_BLACK NW_RGB(0x00, 0x00, 0x00) - -#define NW_YELLOW NW_RGB(0xFF, 0xCC, 0x7B) -#define NW_PURPLE NW_RGB(0x6E, 0x2D, 0x79) -#define NW_BROWN NW_RGB(0x8D, 0x73, 0x50) -#define NW_CYAN NW_RGB(0x00, 0xFF, 0xFF) -#define NW_ORANGE NW_RGB(0xFE, 0x87, 0x1F) -#define NW_PINK NW_RGB(0xFF, 0xAB, 0xB6) -#define NW_MAGENTA NW_RGB(0xFF, 0x05, 0x88) -#define NW_GRAY NW_RGB(0xA7, 0xA7, 0xA7) diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index e6c95921f..46d6bee94 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -14,14 +14,42 @@ #include #include -#include "colorsNW.h" - extern font_t numworks; extern bool is_dwindowed; extern bool is_timered; extern unsigned int timer_altered[9]; +#define DELTAXNW \ + ((DWIDTH - 320) / 2) // we center the NW screen on Casio's screen +#define DELTAYNW 0 // NW screen will be cut in the bottom + +/* Definition of color on Numworks */ + +// Data can be found here +// https://github.com/numworks/epsilon/blob/master/escher/include/escher/palette.h +// and here +// https://github.com/numworks/epsilon/blob/master/python/port/port.cpp#L221 + +#define NW_RGB(r, g, b) (((r >> 3) << 11) | ((g >> 2) << 6) | (b >> 3)) + +#define NW_BLUE NW_RGB(0x50, 0x75, 0xF2) +#define NW_RED NW_RGB(0xFF, 0x00, 0x0C) +#define NW_GREEN NW_RGB(0x50, 0xC1, 0x02) +#define NW_WHITE NW_RGB(0xF7, 0xF9, 0xFA) +#define NW_BLACK NW_RGB(0x00, 0x00, 0x00) + +#define NW_YELLOW NW_RGB(0xFF, 0xCC, 0x7B) +#define NW_PURPLE NW_RGB(0x6E, 0x2D, 0x79) +#define NW_BROWN NW_RGB(0x8D, 0x73, 0x50) +#define NW_CYAN NW_RGB(0x00, 0xFF, 0xFF) +#define NW_ORANGE NW_RGB(0xFE, 0x87, 0x1F) +#define NW_PINK NW_RGB(0xFF, 0xAB, 0xB6) +#define NW_MAGENTA NW_RGB(0xFF, 0x05, 0x88) +#define NW_GRAY NW_RGB(0xA7, 0xA7, 0xA7) + +// There are possibly some others to be listed correctly + static int callback(void) { dupdate(); return TIMER_CONTINUE; @@ -47,10 +75,10 @@ static mp_obj_t Kandinsky_init(void) { dclear(NW_WHITE); struct dwindow nw; - nw.left = 0; - nw.top = 0; - nw.right = 320; - nw.bottom = 240; + nw.left = DELTAXNW; + nw.top = DELTAYNW; + nw.right = 320 + DELTAXNW; + nw.bottom = 240 + DELTAYNW; dwindow_set(nw); is_dwindowed = true; // we mark as windowed @@ -102,7 +130,7 @@ int Internal_Get_Color_From_String(const char *str) { else if (strcmp(str, "brown") == 0) return NW_BROWN; else - return NW_BLACK; + return C_NONE; } int Internal_Treat_Color(mp_obj_t color) { @@ -130,8 +158,8 @@ int Internal_Treat_Color(mp_obj_t color) { } static mp_obj_t Kandinsky_fill_rect(size_t n, mp_obj_t const *args) { - int x = mp_obj_get_int(args[0]); - int y = mp_obj_get_int(args[1]); + int x = mp_obj_get_int(args[0]) + DELTAXNW; + int y = mp_obj_get_int(args[1]) + DELTAYNW; int w = mp_obj_get_int(args[2]); int h = mp_obj_get_int(args[3]); @@ -143,8 +171,8 @@ static mp_obj_t Kandinsky_fill_rect(size_t n, mp_obj_t const *args) { } static mp_obj_t Kandinsky_set_pixel(size_t n, mp_obj_t const *args) { - int x = mp_obj_get_int(args[0]); - int y = mp_obj_get_int(args[1]); + int x = mp_obj_get_int(args[0]) + DELTAXNW; + int y = mp_obj_get_int(args[1]) + DELTAYNW; int color; if (n == 3) @@ -157,8 +185,8 @@ static mp_obj_t Kandinsky_set_pixel(size_t n, mp_obj_t const *args) { } static mp_obj_t Kandinsky_get_pixel(mp_obj_t _x, mp_obj_t _y) { - int x = mp_obj_get_int(_x); - int y = mp_obj_get_int(_y); + int x = mp_obj_get_int(_x) + DELTAXNW; + int y = mp_obj_get_int(_y) + DELTAYNW; if (x >= 0 && x < DWIDTH && y >= 0 && y < DHEIGHT) { color_t color = gint_vram[DWIDTH * y + x]; @@ -168,20 +196,20 @@ static mp_obj_t Kandinsky_get_pixel(mp_obj_t _x, mp_obj_t _y) { } static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { - int x = mp_obj_get_int(args[1]); - int y = mp_obj_get_int(args[2]); + int x = mp_obj_get_int(args[1]) + DELTAXNW; + int y = mp_obj_get_int(args[2]) + DELTAYNW; size_t text_len; char const *text = mp_obj_str_get_data(args[0], &text_len); char *text_free = NULL; /* If there are \n in the text, turn them into spaces */ - if (strchr(text, '\n')) { - text_free = strdup(text); - if (text_free) { - for (size_t i = 0; i < text_len; i++) - text_free[i] = (text_free[i] == '\n') ? ' ' : text_free[i]; - } - } + // if (strchr(text, '\n')) { + // text_free = strdup(text); + // if (text_free) { + // for (size_t i = 0; i < text_len; i++) + // text_free[i] = (text_free[i] == '\n') ? ' ' : text_free[i]; + // } + // } color_t colortext = NW_BLACK; if (n >= 4) { @@ -194,8 +222,25 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { } font_t const *old_font = dfont(&numworks); - dtext_opt(x, y, colortext, colorback, DTEXT_LEFT, DTEXT_TOP, - text_free ? text_free : text, text_len); + // dtext_opt(x, y, colortext, colorback, DTEXT_LEFT, DTEXT_TOP, + // text_free ? text_free : text, text_len); + + // dtext_opt(x, y, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, + // text_free ? text_free : text, text_len); + + int u = 0; + int v = 0; + for (int l = 0; l < (int)text_len; l++) { + if (text[l] == '\n') { + u = 0; + v += 16; + } else { + dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, + &text[l], 1); + u += 10; + } + } + dfont(old_font); free(text_free); From 226d94494face1a2c412251e7f27c426078ef5d2 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Tue, 30 Jan 2024 22:05:37 +0100 Subject: [PATCH 06/27] NW modules for fxCG50 PythonExtra - Ok, now working correctly --- ports/sh/numworks/modkandinsky.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index 46d6bee94..0386012d6 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -31,12 +31,12 @@ extern unsigned int timer_altered[9]; // and here // https://github.com/numworks/epsilon/blob/master/python/port/port.cpp#L221 -#define NW_RGB(r, g, b) (((r >> 3) << 11) | ((g >> 2) << 6) | (b >> 3)) +#define NW_RGB(r, g, b) (((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3)) #define NW_BLUE NW_RGB(0x50, 0x75, 0xF2) #define NW_RED NW_RGB(0xFF, 0x00, 0x0C) #define NW_GREEN NW_RGB(0x50, 0xC1, 0x02) -#define NW_WHITE NW_RGB(0xF7, 0xF9, 0xFA) +#define NW_WHITE NW_RGB(0xFF, 0xFF, 0xFF) #define NW_BLACK NW_RGB(0x00, 0x00, 0x00) #define NW_YELLOW NW_RGB(0xFF, 0xCC, 0x7B) @@ -72,6 +72,7 @@ static mp_obj_t Kandinsky_make_color(color_t color) { static mp_obj_t Kandinsky_init(void) { void pe_enter_graphics_mode(void); pe_enter_graphics_mode(); + dclear(NW_WHITE); struct dwindow nw; @@ -95,7 +96,7 @@ static mp_obj_t Kandinsky_color(size_t n, mp_obj_t const *args) { int r = mp_obj_get_int(args[0]); int g = mp_obj_get_int(args[1]); int b = mp_obj_get_int(args[2]); - int color = C_RGB(r >> 3, g >> 3, b >> 3); + int color = NW_RGB(r, g, b); return mp_obj_new_int(color); } @@ -149,9 +150,9 @@ int Internal_Treat_Color(mp_obj_t color) { size_t tuple_len; mp_obj_t *items; mp_obj_tuple_get(color, &tuple_len, &items); - int r = mp_obj_get_int(items[0]) >> 3; - int g = mp_obj_get_int(items[1]) >> 3; - int b = mp_obj_get_int(items[2]) >> 3; + int r = mp_obj_get_int(items[0]); + int g = mp_obj_get_int(items[1]); + int b = mp_obj_get_int(items[2]); return NW_RGB(r, g, b); } else return NW_BLACK; @@ -200,16 +201,6 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { int y = mp_obj_get_int(args[2]) + DELTAYNW; size_t text_len; char const *text = mp_obj_str_get_data(args[0], &text_len); - char *text_free = NULL; - - /* If there are \n in the text, turn them into spaces */ - // if (strchr(text, '\n')) { - // text_free = strdup(text); - // if (text_free) { - // for (size_t i = 0; i < text_len; i++) - // text_free[i] = (text_free[i] == '\n') ? ' ' : text_free[i]; - // } - // } color_t colortext = NW_BLACK; if (n >= 4) { @@ -222,11 +213,6 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { } font_t const *old_font = dfont(&numworks); - // dtext_opt(x, y, colortext, colorback, DTEXT_LEFT, DTEXT_TOP, - // text_free ? text_free : text, text_len); - - // dtext_opt(x, y, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, - // text_free ? text_free : text, text_len); int u = 0; int v = 0; @@ -235,6 +221,7 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { u = 0; v += 16; } else { + drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback); dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, &text[l], 1); u += 10; @@ -243,7 +230,6 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { dfont(old_font); - free(text_free); return mp_const_none; } From 96077c1653d3a8eb9adb8268083cca3ac722f141 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Tue, 30 Jan 2024 22:55:23 +0100 Subject: [PATCH 07/27] updated .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 2d20cb189..dce5decf8 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ user.props # MacOS desktop metadata files .DS_Store + +# vscode +.vscode/ \ No newline at end of file From 12565ba8d2cd2eb7f78bddc31970cec4508a7064 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Fri, 2 Feb 2024 20:32:15 +0100 Subject: [PATCH 08/27] Huge Cleaning - NW modules on fxCG50 - Ready for PR --- ports/fx9860g3/Makefile | 2 +- ports/fx9860g3/PoliceNW.png | Bin 1419 -> 0 bytes ports/fx9860g3/fxconv-metadata.txt | 9 - ports/fxcg50/PoliceNW.png | Bin 1419 -> 1507 bytes ports/sh/main.c | 662 +++++++++++++++-------------- ports/sh/mpconfigport.h | 125 +++--- ports/sh/numworks/ion.c | 60 ++- ports/sh/numworks/ionkeyNW.h | 52 --- ports/sh/numworks/modkandinsky.c | 6 +- 9 files changed, 464 insertions(+), 452 deletions(-) delete mode 100644 ports/fx9860g3/PoliceNW.png delete mode 100644 ports/sh/numworks/ionkeyNW.h diff --git a/ports/fx9860g3/Makefile b/ports/fx9860g3/Makefile index aaefd7a6f..a49a16024 100644 --- a/ports/fx9860g3/Makefile +++ b/ports/fx9860g3/Makefile @@ -5,7 +5,7 @@ SH_LDFLAGS := -T fx9860g.ld -ljustui-fx -lm -lgint-fx -lc -lgint-fx -lgcc SH_ASSETS := \ img_fkeys_main.png img_modifier_states.png \ - font_5x7.png font_4x4.png font_4x6.png PoliceNW.png + font_5x7.png font_4x4.png font_4x6.png SH_METADATA := fxconv-metadata.txt SH_CONVFLAGS := --fx diff --git a/ports/fx9860g3/PoliceNW.png b/ports/fx9860g3/PoliceNW.png deleted file mode 100644 index b11e4159c55c99dc41bf6bf6db1756ddad8a3dc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1419 zcmV;61$6p}P)003YJ0{{R3t@(+o00001b5ch_0Itp) z=>Px#3{Xr|MgRZ*%*@OH00960|A2LKga7~l1awkPQvm<}|FbPXhyVZu9Z5t%RA_2`S;c^%Y`Y8aW#vx*s&$G9VbpeQxYB0n4qbMXx9Vne zs$=X1S&A`o69ORenn_6zIe2^Hk{SQCLG$q>x94aBSg(&y)Z zBopGP5D+{7O*Ei@+Gcd{NlXqUVjwF(Q)9(=c?BREj>KlMATcD0Cxeo1y^}WvY;y#S z4}oGYV6Zjkl7l&` z%XZG&zGHH}-QKCogg|8+QwxzjAGBga5cOU}K(sz7dYc$J)wM&^q=2o@=4gCI%6xPV z2sZ_wK#Lp=?Zd|?AB){1av0%&tX~31V&OSJzMYo=<6A-t*j5DqH33xxg(z$RSwz%+ zF6#+^&QP#g={#VY0j67CL1U-s3SI*QECy3=@fc8_CIRf5?ExhLR5`_}y5Y zfhVD(`L)3)XA0>0(aWszxW(Gc7&L=EbskCF`V^7GnxUJ+Ypnq^k;G5)ZKcKH^Y2h@ zE`KAN`R4MGGYJHrQo}a)Q;oH`a577+ZZ~fa#949g=O)>AclBQobqfNGWLKxx8qkga zf9C-m;VD3nPXPM}H>o57Ft{*4bgmAN-7Nt21y2OTtu?Yw0Yc6{4d`9~#r3KH1**M~ zgfP?wgKQAiuSAb>Aq#+{6YgzhBoi52P6u* z2&lhT42;BqU~6e4zWBRmwB4I@fE9qANPp&~BCU{x>e&$T_UchJd*8(`a?E!|rz%=1%9Z*GiKp4&dr6<>OfaakRz&;H8 zhUtl;>}I-YT7R>MQ-CV80^DU62@nzn0TrvX5R-tgStCG)RVm&iAT<&gNwmr5bTo}L zt@yAjfZWY!S2#Snej~u%Y2D52$NF!fDOOx5MA%mWGFkJ0yW9=PMa^V0CRw--@N5d` Z%0B|UtM|%P`W*lO002ovPDHLkV1hn_eMkTR diff --git a/ports/fx9860g3/fxconv-metadata.txt b/ports/fx9860g3/fxconv-metadata.txt index 587564b75..ca6799fb6 100644 --- a/ports/fx9860g3/fxconv-metadata.txt +++ b/ports/fx9860g3/fxconv-metadata.txt @@ -31,12 +31,3 @@ font_4x6.png: grid.padding: 1 grid.border: 0 proportional: true - -PoliceNW.png: - name: numworks - type: font - charset: print - grid.size: 10x16 - grid.padding: 0 - grid.border: 0 - proportional: false diff --git a/ports/fxcg50/PoliceNW.png b/ports/fxcg50/PoliceNW.png index b11e4159c55c99dc41bf6bf6db1756ddad8a3dc0..80a72d48fb7e31f31c2b9125e64cadd647d60783 100644 GIT binary patch delta 1436 zcmV;N1!MY)3*!rrS$}m&L_t(&f$f@!vZNpkMK%2YKTo#`xk+viw6|9^)V$Va2}j5# zJxfW7Z2w|qr%1T(_dhR>@k=&`0s<;Q7Bj@(-XnaDo&l6fvtI=M%K9ks_#9ppmf=q@_uO@Ii~ z?g$?P)WNzV&?Q#2fir-(Zmm(`s4gI%gq1Ns|G;&_^jmb83=p5=4j}D@s?+Ru`?R=w z+3X7itcG$J@aQ!TxH_{1KtCh3;=v+roe}WxwW}QfTSHJpBti@W`o<^0;qGhGvc-Ua zH7M}B-xMEfdw&n$m_0BT6(pSM;KlMZ4R0sloxfbPCwdq5`NIrK4G;1v&3Tb!6V zK>GX~kYqwU6>%%yitG*W!(}e|eI9T!I)55) z%Ml0D(31we=-cA8%+H5Ok#LwR%g8`;b+m#@5&xZu;(u*LjTgEkl5+fu75 zaDRFr=vdT0m`O@8SNp}IE0A6yuqcdiDI z-Aw?t1y2OTtp(Ya0A0>M3}_C3;(GM}#Z_A)31O%XcCwwYc6Cp--(^B5;PHDPzBMng ze_ashl^F>Fimq2YbUoQvzpq1pjBpIl8-GnoH4EsiI8hPzrPg%;*>=1L&@3iVW%It8 zdd>upX+RHQ5cqADGv?cBMx^fTj-Gtj&tJ{;U!X3%AHUFP(-7O0} z^X%+%Z1TxmS4qIJjJ;+oROAF;oJF!PK0XcU`@(0<2b^~uT*0%RN8vj7vw-TTNq@=@ zKMls$0BF_h$PUG0DHk(EY7h$Z8bA$oF3Q1&4`wE&i5r~gpgWt>lOqx^$rhV%6~eg> zW-+p{dei8`hcvNdj=072z0CrW$T|TSLi;$Ngy!A~hDpW%LBK2^+r*ax5(S+E)ZZ&k zjKqNW)~=C|A3Z!EHxj9?V#LjO0)KTg2GD0NCALyP>Spd2x!jnQ*8=ttz6;nrqS||0 zlKYu_@SHv^-$+=!g>~s?057gVB4%CjzmR&_m)=QU3utd6;8FdvfYnnk(J3Dx6H~r= zO;QOS1F$uDaUTOD!E6;j3kvDOz!4h8T{|uVY*}p!F#G^h!__(s2%;>Y8-LCKr6t#6 zfbdWWU>oNA2K2O}*6bWfo@*{`z6n5`um@PAi2eitJ>$9!>fkYY2+(cT8KA-HDc&F; zF_NepMu_&))naKRwEBmg0pxB*ySkT0(~kh!gK*J-lWwN$>%WAiSaGKiVLt`PWQ_wB qc{d;zHImIhvhY5@t0|x<{}lkI3yTYoS$`c#L_t(&f$f=#a^oNjMLqKW|MRwSK+@Hf06R|7nPuA8;USJe zFPv>piah>eWl<#D*Zs%kXZ)7Up@0UJAd4Grb&3gL)PHtuxYB0n4qbMXx9Vnes$=X1S&A`o69ORenn_6zIe2^Hk{SQCLG$q>x94aBSg(&y)ZBopGP5D+{7O*Ei@+Gcd{NlXqUVjwF(Q)9(= zc?BREj>KlMATcD0Cxeo1y^}WvY;y#S4}DE`52Vk%@=aPdttIKxI+rDFRzTMub%Y;B>98(LCJs-4ULlE^| zL_oAYDSvvK7&_IpL)4^zt_}9#j3jI3BY=90^orsp?{Aq#+{6YgzhBoOj7JK=?wc*Dv5C{bH|L0 zid+DUvq<&@?bJ=4n1*C4~dXdwOS$QpBjqq8(@)gz54~H4X1Zxwf3t{FfGV^C++`OD z5E2Ff6|1xmlYp>UBS42$Dc&R?H4+#}w8`gmG>tT^_^>O0+|6iLI6S(3BRs&~Y2D52 z$NF!fDOOx5MA%mWGFkJ0yW9=PMa^V0CRw--@N5d`%0B|UtM|%P`W*lO002ovPDHLk FV1mD`Xte+U diff --git a/ports/sh/main.c b/ports/sh/main.c index 58f490ff0..863cd9237 100644 --- a/ports/sh/main.c +++ b/ports/sh/main.c @@ -4,59 +4,60 @@ //.-'` `---` ' License: MIT (except some files; see LICENSE) // //---------------------------------------------------------------------------// -#include "py/builtin.h" #include "py/compile.h" #include "py/gc.h" +#include "py/stackctrl.h" +#include "py/builtin.h" #include "py/mphal.h" #include "py/repl.h" -#include "py/stackctrl.h" -#include "pyexec.h" #include "shared/runtime/gchelper.h" +#include "pyexec.h" #include #include -#include #include #include +#include #include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include "console.h" -#include "debug.h" #include "mpconfigport.h" +#include "console.h" #include "widget_shell.h" +#include "debug.h" //=== Application globals ===// struct pe_globals { - /* The terminal data structure (with lines, editing functions, etc). */ - console_t *console; - /* The global JustUI scene. */ - jscene *scene; - /* The widget shell (which connects the GUI to `console`). */ - widget_shell *shell; - /* The file selection widget. */ - jfileselect *fileselect; - /* Title widget and whether to show it in the shell. */ - jlabel *title; - bool show_title_in_shell; + /* The terminal data structure (with lines, editing functions, etc). */ + console_t *console; + /* The global JustUI scene. */ + jscene *scene; + /* The widget shell (which connects the GUI to `console`). */ + widget_shell *shell; + /* The file selection widget. */ + jfileselect *fileselect; + /* Title widget and whether to show it in the shell. */ + jlabel *title; + bool show_title_in_shell; }; // TODO: Put pe_globals in a header for use by the loop hook in mpconfigport.h widget_shell *pe_shell; -struct pe_globals PE = {0}; +struct pe_globals PE = { 0 }; + // TODO : make this more clean by putting these globals into pe_globals and // making this accessible to modules @@ -64,12 +65,14 @@ bool is_dwindowed = false; bool is_timered = false; unsigned int timer_altered[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + //=== Hook for redirecting stdout/stderr to the shell ===// -static ssize_t stdouterr_write(void *data, void const *buf, size_t size) { - console_t *cons = data; - console_write(cons, buf, size); - return size; +static ssize_t stdouterr_write(void *data, void const *buf, size_t size) +{ + console_t *cons = data; + console_write(cons, buf, size); + return size; } fs_descriptor_type_t stdouterr_type = { @@ -81,67 +84,72 @@ fs_descriptor_type_t stdouterr_type = { //=== File filter for the selection dialog ===// -static bool strendswith(char const *str, char const *suffix) { - size_t l1 = strlen(str); - size_t l2 = strlen(suffix); +static bool strendswith(char const *str, char const *suffix) +{ + size_t l1 = strlen(str); + size_t l2 = strlen(suffix); - return l1 >= l2 && strcmp(str + l1 - l2, suffix) == 0; + return l1 >= l2 && strcmp(str + l1 - l2, suffix) == 0; } -static bool py_file_filter(struct dirent const *ent) { - if (!jfileselect_default_filter(ent)) - return false; - if (ent->d_type == DT_DIR) - return true; - return strendswith(ent->d_name, ".py"); +static bool py_file_filter(struct dirent const *ent) +{ + if(!jfileselect_default_filter(ent)) + return false; + if(ent->d_type == DT_DIR) + return true; + return strendswith(ent->d_name, ".py"); } //=== Module loading utilities ===// -static char *path_to_module(char const *path) { - if (path[0] == '/') - path++; +static char *path_to_module(char const *path) +{ + if(path[0] == '/') + path++; - int i, n = strlen(path); - char *module = malloc(n + 1); - if (!module) - return NULL; + int i, n = strlen(path); + char *module = malloc(n + 1); + if(!module) + return NULL; - for (i = 0; i < n; i++) { - if (i == n - 3 && !strcmp(path + i, ".py")) - break; - module[i] = (path[i] == '/') ? '.' : path[i]; - } - module[i] = 0; - return module; + for(i = 0; i < n; i++) { + if(i == n - 3 && !strcmp(path + i, ".py")) + break; + module[i] = (path[i] == '/') ? '.' : path[i]; + } + module[i] = 0; + return module; } //=== AC/ON interrupt mechanism ===// /* Filter AC/ON push events asynchronously from the keyboard driver and interrupt MicroPython instead. */ -static bool async_filter(key_event_t ev) { - /* Gobble all events related to AC/ON to make sure that the keyboard driver - treats them as handled. Otherwise, we'd run the risk of filling the - event queue (if the user doesn't read from it) thus preventing the - driver from handling AC/ON releases, which disables further presses. */ - if (mp_interrupt_char >= 0 && ev.key == KEY_ACON) { - /* This function supports asynchronous calls, by design. */ - if (ev.type == KEYEV_DOWN) - mp_sched_keyboard_interrupt(); - return false; - } +static bool async_filter(key_event_t ev) +{ + /* Gobble all events related to AC/ON to make sure that the keyboard driver + treats them as handled. Otherwise, we'd run the risk of filling the + event queue (if the user doesn't read from it) thus preventing the + driver from handling AC/ON releases, which disables further presses. */ + if(mp_interrupt_char >= 0 && ev.key == KEY_ACON) { + /* This function supports asynchronous calls, by design. */ + if(ev.type == KEYEV_DOWN) + mp_sched_keyboard_interrupt(); + return false; + } - return true; + return true; } void pe_after_python_exec(int input_kind, int exec_flags, void *ret_val, - int *ret) { - (void)input_kind; - (void)exec_flags; - (void)ret_val; - (void)ret; - clearevents(); + int *ret) +{ + (void)input_kind; + (void)exec_flags; + (void)ret_val; + (void)ret; + clearevents(); } //=== Rendering ===// @@ -156,343 +164,355 @@ extern bopti_image_t const img_modifier_states; #define _(fx, cg) (cg) #endif -void pe_enter_graphics_mode(void) { - /* Cancel any pending update of the shell */ - PE.console->render_needed = false; - PE.shell->widget.update = 0; +void pe_enter_graphics_mode(void) +{ + /* Cancel any pending update of the shell */ + PE.console->render_needed = false; + PE.shell->widget.update = 0; } -void pe_draw(void) { - dclear(C_WHITE); - jscene_render(PE.scene); +void pe_draw(void) +{ + dclear(C_WHITE); + jscene_render(PE.scene); - /* Render shell modifiers above the scene in a convenient spot */ - int shift, alpha, layer; - bool instant; - widget_shell_get_modifiers(PE.shell, &shift, &alpha); - widget_shell_modifier_info(shift, alpha, &layer, &instant); - int icon = 2 * layer + !instant; + /* Render shell modifiers above the scene in a convenient spot */ + int shift, alpha, layer; + bool instant; + widget_shell_get_modifiers(PE.shell, &shift, &alpha); + widget_shell_modifier_info(shift, alpha, &layer, &instant); + int icon = 2 * layer + !instant; #ifdef FX9860G - dsubimage(118, 58, &img_modifier_states, 9 * icon + 1, 1, 8, 6, DIMAGE_NONE); + dsubimage(118, 58, &img_modifier_states, 9*icon+1, 1, 8, 6, + DIMAGE_NONE); #else - dsubimage(377, 207, &img_modifier_states, 16 * icon, 0, 15, 14, DIMAGE_NONE); + dsubimage(377, 207, &img_modifier_states, 16*icon, 0, 15, 14, + DIMAGE_NONE); #endif - dupdate(); + dupdate(); } //=== Application control functions ===// -void pe_restore_window_and_timer(void) { - if (is_dwindowed) { - struct dwindow win; - win.left = 0; - win.top = 0; - win.right = DWIDTH; - win.bottom = DHEIGHT; - dwindow_set(win); - is_dwindowed = false; // we mark as not windowed - } +void pe_restore_window_and_timer(void) +{ + if (is_dwindowed) + { + struct dwindow win; + win.left = 0; + win.top = 0; + win.right = DWIDTH; + win.bottom = DHEIGHT; - if (is_timered) { - for (int u = 0; u < 9; u++) - if (timer_altered[u] == 1) - timer_stop(u); - is_timered = false; - } + dwindow_set(win); + + is_dwindowed = false; // we mark as not windowed + } + + if (is_timered) + { + for (int u = 0; u < 9; u++) + if (timer_altered[u] == 1) + timer_stop(u); + + is_timered = false; + } } -static void pe_reset_micropython(void) { - gc_sweep_all(); - mp_deinit(); - mp_init(); +static void pe_reset_micropython(void) +{ + gc_sweep_all(); + mp_deinit(); + mp_init(); #ifdef FX9860G - char const *msg = "**SHELL INIT.**\n"; + char const *msg = "**SHELL INIT.**\n"; #else - char const *msg = "*** SHELL INITIALIZED ***\n"; + char const *msg = "*** SHELL INITIALIZED ***\n"; #endif - console_newline(PE.console); - console_write(PE.console, msg, -1); - pyexec_event_repl_init(); + console_newline(PE.console); + console_write(PE.console, msg, -1); + pyexec_event_repl_init(); } -static void pe_print_prompt(int which) { - char const *prompt = NULL; - if (which == 2) - prompt = mp_repl_get_ps2(); - else - prompt = mp_repl_get_ps1(); +static void pe_print_prompt(int which) +{ + char const *prompt = NULL; + if(which == 2) + prompt = mp_repl_get_ps2(); + else + prompt = mp_repl_get_ps1(); - console_write(PE.console, prompt, -1); - console_lock_prefix(PE.console); + console_write(PE.console, prompt, -1); + console_lock_prefix(PE.console); } /* Handle a GUI event. If `shell_bound` is true, only actions that have an effect on the shell are allowed and the return value is any full line that is entered in the shell. Otherwise, the full GUI is available and the return value is NULL. */ -static char *pe_handle_event(jevent e, bool shell_bound) { - if (e.type == JSCENE_PAINT) - pe_draw(); +static char *pe_handle_event(jevent e, bool shell_bound) +{ + if(e.type == JSCENE_PAINT) + pe_draw(); - if (e.type == WIDGET_SHELL_MOD_CHANGED) - PE.scene->widget.update = true; + if(e.type == WIDGET_SHELL_MOD_CHANGED) + PE.scene->widget.update = true; - if (e.type == WIDGET_SHELL_INPUT) { - char *line = (char *)e.data; - if (shell_bound) { - return line; - } else { - pyexec_repl_execute(line); - free(line); - pe_print_prompt(1); + if(e.type == WIDGET_SHELL_INPUT) { + char *line = (char *)e.data; + if(shell_bound) { + return line; + } + else { + pyexec_repl_execute(line); + free(line); + pe_print_prompt(1); + } } - } - if (!shell_bound && e.type == JFILESELECT_VALIDATED) { - char const *path = jfileselect_selected_file(PE.fileselect); - char *module = path_to_module(path); - if (module) { - jscene_show_and_focus(PE.scene, PE.shell); - jwidget_set_visible(PE.title, PE.show_title_in_shell); + if(!shell_bound && e.type == JFILESELECT_VALIDATED) { + char const *path = jfileselect_selected_file(PE.fileselect); + char *module = path_to_module(path); + if(module) { + jscene_show_and_focus(PE.scene, PE.shell); + jwidget_set_visible(PE.title, PE.show_title_in_shell); - pe_reset_micropython(); + pe_reset_micropython(); - char *str = malloc(8 + strlen(module) + 1); - if (str) { - strcpy(str, "import "); - strcat(str, module); - pyexec_repl_execute(str); - free(str); - } - free(module); + char *str = malloc(8 + strlen(module) + 1); + if(str) { + strcpy(str, "import "); + strcat(str, module); + pyexec_repl_execute(str); + free(str); + } + free(module); - pe_restore_window_and_timer(); + pe_restore_window_and_timer(); - pe_print_prompt(1); + pe_print_prompt(1); + } + } + + if(e.type != JWIDGET_KEY || e.key.type == KEYEV_UP) + return NULL; + int key = e.key.key; + + if(key == KEY_SQUARE && !e.key.shift && e.key.alpha) + pe_debug_screenshot(); + if(key == KEY_TAN) + pe_debug_kmalloc(); + + if(!shell_bound && key == KEY_F1) { + jscene_show_and_focus(PE.scene, PE.fileselect); + jwidget_set_visible(PE.title, true); + } + if(!shell_bound && key == KEY_F2) { + jscene_show_and_focus(PE.scene, PE.shell); + jwidget_set_visible(PE.title, PE.show_title_in_shell); } - } - if (e.type != JWIDGET_KEY || e.key.type == KEYEV_UP) return NULL; - int key = e.key.key; +} - if (key == KEY_SQUARE && !e.key.shift && e.key.alpha) - pe_debug_screenshot(); - if (key == KEY_TAN) +int pe_readline(vstr_t *line, char const *prompt) +{ + console_write(PE.console, prompt, -1); + console_lock_prefix(PE.console); + + int c = mp_interrupt_char; + mp_hal_set_interrupt_char(-1); + + char *text = NULL; + while(!text) { + jevent e = jscene_run(PE.scene); + text = pe_handle_event(e, true); + } + + mp_hal_set_interrupt_char(c); + vstr_reset(line); + vstr_add_str(line, text); + free(text); + return 0; // TODO: return CHAR_CTRL_C on AC/ON instead +} + +int main(int argc, char **argv) +{ + pe_debug_init(); pe_debug_kmalloc(); - if (!shell_bound && key == KEY_F1) { - jscene_show_and_focus(PE.scene, PE.fileselect); - jwidget_set_visible(PE.title, true); - } - if (!shell_bound && key == KEY_F2) { - jscene_show_and_focus(PE.scene, PE.shell); - jwidget_set_visible(PE.title, PE.show_title_in_shell); - } + //=== Init sequence ===// - return NULL; -} + keydev_set_async_filter(keydev_std(), async_filter); -int pe_readline(vstr_t *line, char const *prompt) { - console_write(PE.console, prompt, -1); - console_lock_prefix(PE.console); + PE.console = console_create(8192, 200); - int c = mp_interrupt_char; - mp_hal_set_interrupt_char(-1); + /* Set up standard streams */ + close(STDOUT_FILENO); + close(STDERR_FILENO); + open_generic(&stdouterr_type, PE.console, STDOUT_FILENO); + open_generic(&stdouterr_type, PE.console, STDERR_FILENO); - char *text = NULL; - while (!text) { - jevent e = jscene_run(PE.scene); - text = pe_handle_event(e, true); - } - - mp_hal_set_interrupt_char(c); - vstr_reset(line); - vstr_add_str(line, text); - free(text); - return 0; // TODO: return CHAR_CTRL_C on AC/ON instead -} - -int main(int argc, char **argv) { - pe_debug_init(); - pe_debug_kmalloc(); - - //=== Init sequence ===// - - keydev_set_async_filter(keydev_std(), async_filter); - - /* Make delayed shift/alpha permanent so the stay between calls to - jscene_run() and can be used for delayed key combos */ - keydev_transform_t tr = keydev_transform(keydev_std()); - tr.enabled |= KEYDEV_TR_DELAYED_SHIFT; - tr.enabled |= KEYDEV_TR_DELAYED_ALPHA; - keydev_set_transform(keydev_std(), tr); - - PE.console = console_create(8192, 200); - - /* Set up standard streams */ - close(STDOUT_FILENO); - close(STDERR_FILENO); - open_generic(&stdouterr_type, PE.console, STDOUT_FILENO); - open_generic(&stdouterr_type, PE.console, STDERR_FILENO); - - /* Initialize the MicroPython GC with most available memory */ - mp_stack_ctrl_init(); + /* Initialize the MicroPython GC with most available memory */ + mp_stack_ctrl_init(); #ifdef FX9860G - /* Put basically all the OS heap into the Python GC. For our own purposes - we'll use gint's _uram arena and the leftover from the OS heap. */ - int size = 65536; - bool first = true; + /* Put basically all the OS heap into the Python GC. For our own purposes + we'll use gint's _uram arena and the leftover from the OS heap. */ + int size = 65536; + bool first = true; - while (size >= 2048) { - void *area = kmalloc(size, "_os"); - if (area) { - if (first) - gc_init(area, area + size); - else - gc_add(area, area + size); - first = false; - } else - size /= 2; - } + while(size >= 2048) { + void *area = kmalloc(size, "_os"); + if(area) { + if(first) + gc_init(area, area + size); + else + gc_add(area, area + size); + first = false; + } + else size /= 2; + } - if (first) - pe_debug_panic("No heap!"); + if(first) + pe_debug_panic("No heap!"); #if PE_DEBUG - /* Add some Python ram */ - // https://www.planet-casio.com/Fr/forums/topic15269-10-khicas-add-in-calcul-formel-pour-graph-90e-et-35eii.html#189284 - void *py_ram_start = (void *)0x88053800; - void *py_ram_end = (void *)0x8807f000; - gc_add(py_ram_start, py_ram_end); + /* Add some Python ram */ + // https://www.planet-casio.com/Fr/forums/topic15269-10-khicas-add-in-calcul-formel-pour-graph-90e-et-35eii.html#189284 + void *py_ram_start = (void*)0x88053800; + void *py_ram_end = (void*)0x8807f000; + gc_add(py_ram_start, py_ram_end); #endif #else - /* Get everything from the OS stack (~ 350 ko) */ - size_t gc_area_size; - void *gc_area = kmalloc_max(&gc_area_size, "_ostk"); - gc_init(gc_area, gc_area + gc_area_size); + /* Get everything from the OS stack (~ 350 ko) */ + size_t gc_area_size; + void *gc_area = kmalloc_max(&gc_area_size, "_ostk"); + gc_init(gc_area, gc_area + gc_area_size); - /* Other options: - - All of _uram (leaving the OS heap for the shell/GUI/etc) - - The OS' extra VRAM - - Memory past the 2 MB boundary on tested OSes */ - // gc_add(start, end)... + /* Other options: + - All of _uram (leaving the OS heap for the shell/GUI/etc) + - The OS' extra VRAM + - Memory past the 2 MB boundary on tested OSes */ + // gc_add(start, end)... #endif - mp_init(); + mp_init(); - /* TODO: Add an option for the shorter prompt */ + /* TODO: Add an option for the shorter prompt */ #ifdef FX9860G - MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS1]) = - MP_OBJ_NEW_QSTR(qstr_from_str(">")); - MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS2]) = - MP_OBJ_NEW_QSTR(qstr_from_str(".")); + MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS1]) = + MP_OBJ_NEW_QSTR(qstr_from_str(">")); + MP_STATE_VM(sys_mutable[MP_SYS_MUTABLE_PS2]) = + MP_OBJ_NEW_QSTR(qstr_from_str(".")); #endif - pyexec_event_repl_init(); - pe_print_prompt(1); + pyexec_event_repl_init(); + pe_print_prompt(1); - //=== GUI setup ===// + //=== GUI setup ===// - PE.scene = jscene_create_fullscreen(NULL); - PE.title = jlabel_create("PythonExtra", PE.scene); - jwidget *stack = jwidget_create(PE.scene); - jfkeys *fkeys = jfkeys_create2(&img_fkeys_main, "/FILES;/SHELL", PE.scene); - (void)fkeys; + PE.scene = jscene_create_fullscreen(NULL); + PE.title = jlabel_create("PythonExtra", PE.scene); + jwidget *stack = jwidget_create(PE.scene); + jfkeys *fkeys = jfkeys_create2(&img_fkeys_main, "/FILES;/SHELL", PE.scene); + (void)fkeys; - jwidget_set_stretch(PE.title, 1, 0, false); + jwidget_set_stretch(PE.title, 1, 0, false); - jlayout_set_vbox(PE.scene)->spacing = _(0, 3); - jlayout_set_stack(stack); - jwidget_set_stretch(stack, 1, 1, false); + jlayout_set_vbox(PE.scene)->spacing = _(0, 3); + jlayout_set_stack(stack); + jwidget_set_stretch(stack, 1, 1, false); - /* Filesystem tab */ - PE.fileselect = jfileselect_create(stack); - jfileselect_set_filter(PE.fileselect, py_file_filter); - jfileselect_set_show_file_size(PE.fileselect, true); - jwidget_set_stretch(PE.fileselect, 1, 1, false); + /* Filesystem tab */ + PE.fileselect = jfileselect_create(stack); + jfileselect_set_filter(PE.fileselect, py_file_filter); + jfileselect_set_show_file_size(PE.fileselect, true); + jwidget_set_stretch(PE.fileselect, 1, 1, false); - /* Shell tab */ - PE.shell = pe_shell = widget_shell_create(PE.console, stack); - widget_shell_set_line_spacing(PE.shell, _(1, 3)); - jwidget_set_stretch(PE.shell, 1, 1, false); + /* Shell tab */ + PE.shell = pe_shell = widget_shell_create(PE.console, stack); + widget_shell_set_line_spacing(PE.shell, _(1, 3)); + jwidget_set_stretch(PE.shell, 1, 1, false); #ifdef FX9860G - PE.show_title_in_shell = false; - jwidget_set_padding(PE.title, 0, 0, 1, 0); - widget_shell_set_font(PE.shell, &font_4x6); + PE.show_title_in_shell = false; + jwidget_set_padding(PE.title, 0, 0, 1, 0); + widget_shell_set_font(PE.shell, &font_4x6); #else - PE.show_title_in_shell = true; - jwidget_set_background(PE.title, C_BLACK); - jlabel_set_text_color(PE.title, C_WHITE); - jwidget_set_padding(PE.title, 3, 6, 3, 6); - jwidget_set_padding(stack, 0, 6, 0, 6); + PE.show_title_in_shell = true; + jwidget_set_background(PE.title, C_BLACK); + jlabel_set_text_color(PE.title, C_WHITE); + jwidget_set_padding(PE.title, 3, 6, 3, 6); + jwidget_set_padding(stack, 0, 6, 0, 6); #endif - /* Initial state */ - jfileselect_browse(PE.fileselect, "/"); - jscene_show_and_focus(PE.scene, PE.fileselect); + /* Initial state */ + jfileselect_browse(PE.fileselect, "/"); + jscene_show_and_focus(PE.scene, PE.fileselect); - //=== Event handling ===// + //=== Event handling ===// - while (1) { - jevent e = jscene_run(PE.scene); - pe_handle_event(e, false); - } + while(1) { + jevent e = jscene_run(PE.scene); + pe_handle_event(e, false); + } - //=== Deinitialization ===// + //=== Deinitialization ===// - gc_sweep_all(); - mp_deinit(); - console_destroy(PE.console); - return 0; + gc_sweep_all(); + mp_deinit(); + console_destroy(PE.console); + return 0; } /* Handle uncaught exceptions (normally unreachable). */ -void nlr_jump_fail(void *val) { - dclear(C_BLACK); - dtext(2, 2, C_WHITE, "nlr_jump_fail!"); - dprint(2, 2, C_WHITE, "val = %p", val); - dupdate(); - while (1) - getkey(); +void nlr_jump_fail(void *val) +{ + dclear(C_BLACK); + dtext(2, 2, C_WHITE, "nlr_jump_fail!"); + dprint(2, 2, C_WHITE, "val = %p", val); + dupdate(); + while(1) + getkey(); } /* Do a garbage collection cycle. */ -void gc_collect(void) { - gc_collect_start(); - gc_helper_collect_regs_and_stack(); - gc_collect_end(); +void gc_collect(void) +{ + gc_collect_start(); + gc_helper_collect_regs_and_stack(); + gc_collect_end(); } -mp_import_stat_t mp_import_stat(const char *path) { - struct stat st; - int rc = stat(path, &st); - if (rc < 0) - return MP_IMPORT_STAT_NO_EXIST; +mp_import_stat_t mp_import_stat(const char *path) +{ + struct stat st; + int rc = stat(path, &st); + if(rc < 0) + return MP_IMPORT_STAT_NO_EXIST; - if (S_ISDIR(st.st_mode)) - return MP_IMPORT_STAT_DIR; - else - return MP_IMPORT_STAT_FILE; + if(S_ISDIR(st.st_mode)) + return MP_IMPORT_STAT_DIR; + else + return MP_IMPORT_STAT_FILE; } // TODO: See branch 'posix-open' for a relevant attempt at using the POSIX API -mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, - mp_map_t *kwargs) { - mp_obj_t *args_items; - size_t len; - mp_obj_get_array(*args, &len, &args_items); - printf("%d %p\n", (int)len, args_items); - if (len != 2) +mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) +{ + mp_obj_t *args_items; + size_t len; + mp_obj_get_array(*args, &len, &args_items); + printf("%d %p\n", (int)len, args_items); + if(len != 2) + return mp_const_none; + + char const *path = mp_obj_str_get_str(args_items[0]); + char const *mode = mp_obj_str_get_str(args_items[1]); + printf("'%s' '%s'\n", path, mode); + return mp_const_none; - - char const *path = mp_obj_str_get_str(args_items[0]); - char const *mode = mp_obj_str_get_str(args_items[1]); - printf("'%s' '%s'\n", path, mode); - - return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); diff --git a/ports/sh/mpconfigport.h b/ports/sh/mpconfigport.h index 4e84a7eba..31da969b3 100644 --- a/ports/sh/mpconfigport.h +++ b/ports/sh/mpconfigport.h @@ -5,109 +5,106 @@ //---------------------------------------------------------------------------// // pe.mpconfigport: MicroPython's main port configuration file -#include "widget_shell.h" -#include #include +#include +#include "widget_shell.h" /* Debugging options: PythonExtra debug tools (pretty much required for any other one), MicroPython's verbose logging. */ /* PythonExtra's main debug flag */ -#define PE_DEBUG (0) -#define MICROPY_DEBUG_VERBOSE (0) +#define PE_DEBUG (0) +#define MICROPY_DEBUG_VERBOSE (0) /* Custom flag to remove DEBUG_printf in alloc/GC (very verbose) */ -#define MICROPY_DEBUG_VERBOSE_ALLOC (0) +#define MICROPY_DEBUG_VERBOSE_ALLOC (0) #if PE_DEBUG extern const struct _mp_print_t mp_debug_print; -#define MICROPY_DEBUG_PRINTER (&mp_debug_print) +#define MICROPY_DEBUG_PRINTER (&mp_debug_print) #endif /* Custom option to use relative imports. For instance when working at the fs root, 'import b' in '/folder/a.py' will import 'folder/b.py' not '/b.py'. */ -#define MICROPY_RELATIVE_FILE_IMPORTS (1) +#define MICROPY_RELATIVE_FILE_IMPORTS (1) /* General feature set selection Other options: BASIC_FEATURES, EXTRA_FEATURES, FULL_FEATURES, EVERYTHING */ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_CORE_FEATURES) /* Main features */ -#define MICROPY_ENABLE_COMPILER (1) -#define MICROPY_ENABLE_GC (1) -#define MICROPY_GC_SPLIT_HEAP (1) -#define MP_ENDIANNESS_BIG (1) -#define MICROPY_READER_POSIX (1) -#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) -#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) -#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) -#define MICROPY_REPL_EVENT_DRIVEN (1) +#define MICROPY_ENABLE_COMPILER (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_GC_SPLIT_HEAP (1) +#define MP_ENDIANNESS_BIG (1) +#define MICROPY_READER_POSIX (1) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE) +#define MICROPY_REPL_EVENT_DRIVEN (1) /* Other features that we select against MICROPY_CONFIG_ROM_LEVEL */ -#define MICROPY_PY_FSTRINGS (1) /* in EXTRA_FEATURES */ -#define MICROPY_HELPER_REPL (1) /* in EXTRA_FEATURES */ -#define MICROPY_ENABLE_SOURCE_LINE (1) /* in EXTRA_FEATURES */ -#define MICROPY_PY_BUILTINS_STR_UNICODE (1) /* in EXTRA_FEATURES */ -#define MICROPY_PY_BUILTINS_HELP_MODULES (1) /* in EXTRA_FEATURES */ -#define MICROPY_KBD_EXCEPTION (1) /* in EXTRA_FEATURES */ -#define MICROPY_PY_SYS_PS1_PS2 (1) /* in EXTRA_FEATURES */ -#define MICROPY_MODULE_BUILTIN_INIT (1) /* in EXTRA_FEATURES */ -#define MICROPY_PY_ALL_SPECIAL_METHODS (1) /* in EXTRA_FEATURES */ +#define MICROPY_PY_FSTRINGS (1) /* in EXTRA_FEATURES */ +#define MICROPY_HELPER_REPL (1) /* in EXTRA_FEATURES */ +#define MICROPY_ENABLE_SOURCE_LINE (1) /* in EXTRA_FEATURES */ +#define MICROPY_PY_BUILTINS_STR_UNICODE (1) /* in EXTRA_FEATURES */ +#define MICROPY_PY_BUILTINS_HELP_MODULES (1) /* in EXTRA_FEATURES */ +#define MICROPY_KBD_EXCEPTION (1) /* in EXTRA_FEATURES */ +#define MICROPY_PY_SYS_PS1_PS2 (1) /* in EXTRA_FEATURES */ +#define MICROPY_MODULE_BUILTIN_INIT (1) /* in EXTRA_FEATURES */ +#define MICROPY_PY_ALL_SPECIAL_METHODS (1) /* in EXTRA_FEATURES */ #define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) /* in EXTRA_FEATURES */ -#define MICROPY_PY_BUILTINS_ROUND_INT (1) /* in EXTRA_FEATURES */ +#define MICROPY_PY_BUILTINS_ROUND_INT (1) /* in EXTRA_FEATURES */ // #define MICROPY_PY_SYS_STDFILES (1) /* in EXTRA_FEATURES */ -#define MICROPY_ALLOC_PATH_MAX (256) -#define MICROPY_ALLOC_PARSE_CHUNK_INIT (32) -#define MICROPY_MEM_STATS (0) -#define MICROPY_GC_ALLOC_THRESHOLD (1) -#define MICROPY_ENABLE_DOC_STRING (0) +#define MICROPY_ALLOC_PATH_MAX (256) +#define MICROPY_ALLOC_PARSE_CHUNK_INIT (32) +#define MICROPY_MEM_STATS (0) +#define MICROPY_GC_ALLOC_THRESHOLD (1) +#define MICROPY_ENABLE_DOC_STRING (0) #define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (1) -#define MICROPY_PY_BUILTINS_BYTEARRAY (1) -#define MICROPY_PY_BUILTINS_ENUMERATE (1) -#define MICROPY_PY_BUILTINS_FILTER (1) -#define MICROPY_PY_BUILTINS_FROZENSET (1) -#define MICROPY_PY_BUILTINS_HELP (1) -#define MICROPY_PY_BUILTINS_INPUT (1) -#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) -#define MICROPY_PY_BUILTINS_MIN_MAX (1) -#define MICROPY_PY_BUILTINS_PROPERTY (1) -#define MICROPY_PY_BUILTINS_REVERSED (1) -#define MICROPY_PY_BUILTINS_SET (1) -#define MICROPY_PY_BUILTINS_SLICE (1) +#define MICROPY_PY_BUILTINS_BYTEARRAY (1) +#define MICROPY_PY_BUILTINS_ENUMERATE (1) +#define MICROPY_PY_BUILTINS_FILTER (1) +#define MICROPY_PY_BUILTINS_FROZENSET (1) +#define MICROPY_PY_BUILTINS_HELP (1) +#define MICROPY_PY_BUILTINS_INPUT (1) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_BUILTINS_MIN_MAX (1) +#define MICROPY_PY_BUILTINS_PROPERTY (1) +#define MICROPY_PY_BUILTINS_REVERSED (1) +#define MICROPY_PY_BUILTINS_SET (1) +#define MICROPY_PY_BUILTINS_SLICE (1) /* Extra built-in modules */ -#define MICROPY_PY_ARRAY (1) -#define MICROPY_PY_COLLECTIONS (1) -#define MICROPY_PY_MATH (1) -#define MICROPY_PY_CMATH (1) -#define MICROPY_PY_GC (1) -#define MICROPY_PY_IO (1) -#define MICROPY_PY_STRUCT (1) -#define MICROPY_PY_RANDOM (1) -#define MICROPY_PY_RANDOM_EXTRA_FUNCS (1) -#define MICROPY_PY_SYS (1) -#define MICROPY_PY_TIME (1) +#define MICROPY_PY_ARRAY (1) +#define MICROPY_PY_COLLECTIONS (1) +#define MICROPY_PY_MATH (1) +#define MICROPY_PY_CMATH (1) +#define MICROPY_PY_GC (1) +#define MICROPY_PY_IO (1) +#define MICROPY_PY_STRUCT (1) +#define MICROPY_PY_RANDOM (1) +#define MICROPY_PY_RANDOM_EXTRA_FUNCS (1) +#define MICROPY_PY_SYS (1) +#define MICROPY_PY_TIME (1) // TODO: Enable the os module: // #define MICROPY_PY_UOS (1) // TODO: Enable other modules // #define MICROPY_PY_URE (1) // + other flags? /* Enable alias of u-modules */ -#define MICROPY_MODULE_WEAK_LINKS (1) +#define MICROPY_MODULE_WEAK_LINKS (1) /* Command executed automatically after every shell input */ -void pe_after_python_exec(int input_kind, int exec_flags, void *ret_val, - int *ret); +void pe_after_python_exec( + int input_kind, int exec_flags, void *ret_val, int *ret); #define MICROPY_BOARD_AFTER_PYTHON_EXEC pe_after_python_exec /* Command executed regularly during execution */ extern void pe_draw(void); extern widget_shell *pe_shell; -#define MICROPY_VM_HOOK_LOOP \ - { \ - if (pe_shell->widget.update) \ - pe_draw(); \ - } +#define MICROPY_VM_HOOK_LOOP \ + { if(pe_shell->widget.update) pe_draw(); } /* extra built in names to add to the global namespace #define MICROPY_PORT_BUILTINS \ @@ -119,6 +116,6 @@ typedef uintptr_t mp_uint_t; typedef long mp_off_t; #define MICROPY_HW_BOARD_NAME "sh7305" -#define MICROPY_HW_MCU_NAME "sh-4a" +#define MICROPY_HW_MCU_NAME "sh-4a" -#define MP_STATE_PORT MP_STATE_VM \ No newline at end of file +#define MP_STATE_PORT MP_STATE_VM diff --git a/ports/sh/numworks/ion.c b/ports/sh/numworks/ion.c index f381c936d..b86315d19 100644 --- a/ports/sh/numworks/ion.c +++ b/ports/sh/numworks/ion.c @@ -14,11 +14,67 @@ #include "py/runtime.h" #include -#include "ionkeyNW.h" - #include #include +/* BEGINING OF KEY TRANSLATION */ + +// the following table aims at providing a keymap for NW on Casio +// line that are commented correspond to keys that are similar (with exact same +// name) between NW and Casio + +//#define KEY_LEFT +//#define KEY_UP +//#define KEY_DOWN +//#define KEY_RIGHT +#define KEY_OK KEY_F1 +#define KEY_BACK KEY_EXIT +#define KEY_HOME KEY_MENU +#define KEY_ONOFF KEY_ACON +//#define KEY_SHIFT +//#define KEY_ALPHA +#define KEY_XNT KEY_XOT +#define KEY_VAR KEY_VARS +#define KEY_TOOLBOX KEY_OPTN +#define KEY_BACKSPACE KEY_DEL +//#define KEY_EXP // Note this one may be challenging +//#define KEY_LN +//#define KEY_LOG +#define KEY_IMAGINARY KEY_F2 +//#define KEY_COMMA +//#define KEY_POWER +#define KEY_SINE KEY_SIN +#define KEY_COSINE KEY_COS +#define KEY_TANGENT KEY_TAN +#define KEY_PI KEY_F3 +#define KEY_SQRT KEY_F4 +//#define KEY_SQUARE +#define KEY_SEVEN KEY_7 +#define KEY_EIGHT KEY_8 +#define KEY_NINE KEY_9 +#define KEY_LEFTPARENTHESIS KEY_LEFTP +#define KEY_RIGHTPARENTHESIS KEY_RIGHTP +#define KEY_FOUR KEY_4 +#define KEY_FIVE KEY_5 +#define KEY_SIX KEY_6 +#define KEY_MULTIPLICATION KEY_MUL +#define KEY_DIVISION KEY_DIV +#define KEY_ONE KEY_1 +#define KEY_TWO KEY_2 +#define KEY_THREE KEY_3 +#define KEY_PLUS KEY_ADD +#define KEY_MINUS KEY_SUB +#define KEY_ZERO KEY_0 +//#define KEY_DOT +#define KEY_EE KEY_F5 +#define KEY_ANS KEY_NEG +//#define KEY_EXE + + +/* END OF KEY TRANSLATION */ + + + #define FUN_0(NAME) MP_DEFINE_CONST_FUN_OBJ_0(ion_##NAME##_obj, ion_##NAME) #define FUN_1(NAME) MP_DEFINE_CONST_FUN_OBJ_1(ion_##NAME##_obj, ion_##NAME) #define FUN_2(NAME) MP_DEFINE_CONST_FUN_OBJ_2(ion_##NAME##_obj, ion_##NAME) diff --git a/ports/sh/numworks/ionkeyNW.h b/ports/sh/numworks/ionkeyNW.h deleted file mode 100644 index 1b9ae3c56..000000000 --- a/ports/sh/numworks/ionkeyNW.h +++ /dev/null @@ -1,52 +0,0 @@ -#include - -// the following table aims at providing a keymap for NW on Casio -// line that are commented correspond to keys that are similar (with exact same -// name) between NW and Casio - -//#define KEY_LEFT -//#define KEY_UP -//#define KEY_DOWN -//#define KEY_RIGHT -#define KEY_OK KEY_F1 -#define KEY_BACK KEY_EXIT -#define KEY_HOME KEY_MENU -#define KEY_ONOFF KEY_ACON -//#define KEY_SHIFT -//#define KEY_ALPHA -#define KEY_XNT KEY_XOT -#define KEY_VAR KEY_VARS -#define KEY_TOOLBOX KEY_OPTN -#define KEY_BACKSPACE KEY_DEL -//#define KEY_EXP // Note this one may be challenging -//#define KEY_LN -//#define KEY_LOG -#define KEY_IMAGINARY KEY_F2 -//#define KEY_COMMA -//#define KEY_POWER -#define KEY_SINE KEY_SIN -#define KEY_COSINE KEY_COS -#define KEY_TANGENT KEY_TAN -#define KEY_PI KEY_F3 -#define KEY_SQRT KEY_F4 -//#define KEY_SQUARE -#define KEY_SEVEN KEY_7 -#define KEY_EIGHT KEY_8 -#define KEY_NINE KEY_9 -#define KEY_LEFTPARENTHESIS KEY_LEFTP -#define KEY_RIGHTPARENTHESIS KEY_RIGHTP -#define KEY_FOUR KEY_4 -#define KEY_FIVE KEY_5 -#define KEY_SIX KEY_6 -#define KEY_MULTIPLICATION KEY_MUL -#define KEY_DIVISION KEY_DIV -#define KEY_ONE KEY_1 -#define KEY_TWO KEY_2 -#define KEY_THREE KEY_3 -#define KEY_PLUS KEY_ADD -#define KEY_MINUS KEY_SUB -#define KEY_ZERO KEY_0 -//#define KEY_DOT -#define KEY_EE KEY_F5 -#define KEY_ANS KEY_NEG -//#define KEY_EXE \ No newline at end of file diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index 0386012d6..18a4cc7f6 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -166,7 +166,7 @@ static mp_obj_t Kandinsky_fill_rect(size_t n, mp_obj_t const *args) { int color = Internal_Treat_Color(args[4]); - drect(x, y, x + w, y + h, color); + drect(x, y, x + w - 1, y + h - 1, color); return mp_const_none; } @@ -197,8 +197,8 @@ static mp_obj_t Kandinsky_get_pixel(mp_obj_t _x, mp_obj_t _y) { } static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { - int x = mp_obj_get_int(args[1]) + DELTAXNW; - int y = mp_obj_get_int(args[2]) + DELTAYNW; + int x = mp_obj_get_int(args[1]) + DELTAXNW + 1; // values used to adjust the visual result as per actual NW + int y = mp_obj_get_int(args[2]) + DELTAYNW + 2; // values used to adjust the visual result as per actual NW size_t text_len; char const *text = mp_obj_str_get_data(args[0], &text_len); From e3fd785200b4b0b9a5460a93c12105cf9f533aa4 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Fri, 2 Feb 2024 20:36:19 +0100 Subject: [PATCH 09/27] Huge Cleaning - NW modules on fxCG50 - Ready for PR - Phase II --- ports/sh/main.c | 7 + ports/sh/modgint.c | 446 ++++++++++++++++++++++----------------------- 2 files changed, 229 insertions(+), 224 deletions(-) diff --git a/ports/sh/main.c b/ports/sh/main.c index 863cd9237..e63b5fcfb 100644 --- a/ports/sh/main.c +++ b/ports/sh/main.c @@ -347,6 +347,13 @@ int main(int argc, char **argv) keydev_set_async_filter(keydev_std(), async_filter); + /* Make delayed shift/alpha permanent so the stay between calls to + jscene_run() and can be used for delayed key combos */ + keydev_transform_t tr = keydev_transform(keydev_std()); + tr.enabled |= KEYDEV_TR_DELAYED_SHIFT; + tr.enabled |= KEYDEV_TR_DELAYED_ALPHA; + keydev_set_transform(keydev_std(), tr); + PE.console = console_create(8192, 200); /* Set up standard streams */ diff --git a/ports/sh/modgint.c b/ports/sh/modgint.c index 725faaca0..768f22a0b 100644 --- a/ports/sh/modgint.c +++ b/ports/sh/modgint.c @@ -9,120 +9,138 @@ // considered relevant for high-level Python development). //--- -#include "console.h" -#include "py/objtuple.h" #include "py/runtime.h" +#include "py/objtuple.h" #include #include -#include -#include - void pe_enter_graphics_mode(void); -#define FUN_0(NAME) \ - MP_DEFINE_CONST_FUN_OBJ_0(modgint_##NAME##_obj, modgint_##NAME) -#define FUN_1(NAME) \ - MP_DEFINE_CONST_FUN_OBJ_1(modgint_##NAME##_obj, modgint_##NAME) -#define FUN_2(NAME) \ - MP_DEFINE_CONST_FUN_OBJ_2(modgint_##NAME##_obj, modgint_##NAME) -#define FUN_3(NAME) \ - MP_DEFINE_CONST_FUN_OBJ_3(modgint_##NAME##_obj, modgint_##NAME) -#define FUN_VAR(NAME, MIN) \ - MP_DEFINE_CONST_FUN_OBJ_VAR(modgint_##NAME##_obj, MIN, modgint_##NAME) -#define FUN_BETWEEN(NAME, MIN, MAX) \ - MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modgint_##NAME##_obj, MIN, MAX, \ - modgint_##NAME) +#define FUN_0(NAME) \ + MP_DEFINE_CONST_FUN_OBJ_0(modgint_ ## NAME ## _obj, modgint_ ## NAME) +#define FUN_1(NAME) \ + MP_DEFINE_CONST_FUN_OBJ_1(modgint_ ## NAME ## _obj, modgint_ ## NAME) +#define FUN_2(NAME) \ + MP_DEFINE_CONST_FUN_OBJ_2(modgint_ ## NAME ## _obj, modgint_ ## NAME) +#define FUN_3(NAME) \ + MP_DEFINE_CONST_FUN_OBJ_3(modgint_ ## NAME ## _obj, modgint_ ## NAME) +#define FUN_VAR(NAME, MIN) \ + MP_DEFINE_CONST_FUN_OBJ_VAR(modgint_ ## NAME ## _obj, MIN, \ + modgint_ ## NAME) +#define FUN_BETWEEN(NAME, MIN, MAX) \ + MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modgint_ ## NAME ## _obj, MIN, MAX, \ + modgint_ ## NAME) -STATIC mp_obj_t modgint___init__(void) { - pe_enter_graphics_mode(); - dclear(C_WHITE); - return mp_const_none; +STATIC mp_obj_t modgint___init__(void) +{ + pe_enter_graphics_mode(); + dclear(C_WHITE); + return mp_const_none; } /* */ STATIC qstr const key_event_fields[] = { - MP_QSTR_time, MP_QSTR_mod, MP_QSTR_shift, - MP_QSTR_alpha, MP_QSTR_type, MP_QSTR_key, + MP_QSTR_time, + MP_QSTR_mod, + MP_QSTR_shift, + MP_QSTR_alpha, + MP_QSTR_type, + MP_QSTR_key, }; -STATIC mp_obj_t mk_key_event(key_event_t ev) { - mp_obj_t items[] = { - mp_obj_new_int(ev.time), mp_obj_new_bool(ev.mod), - mp_obj_new_bool(ev.shift), mp_obj_new_bool(ev.alpha), - mp_obj_new_int(ev.type), mp_obj_new_int(ev.key), - }; - return mp_obj_new_attrtuple(key_event_fields, 6, items); +STATIC mp_obj_t mk_key_event(key_event_t ev) +{ + mp_obj_t items[] = { + mp_obj_new_int(ev.time), + mp_obj_new_bool(ev.mod), + mp_obj_new_bool(ev.shift), + mp_obj_new_bool(ev.alpha), + mp_obj_new_int(ev.type), + mp_obj_new_int(ev.key), + }; + return mp_obj_new_attrtuple(key_event_fields, 6, items); } -STATIC mp_obj_t modgint_pollevent(void) { - key_event_t ev = pollevent(); - return mk_key_event(ev); +STATIC mp_obj_t modgint_pollevent(void) +{ + key_event_t ev = pollevent(); + return mk_key_event(ev); } // TODO: waitevent: timeout parameter? -STATIC mp_obj_t modgint_clearevents(void) { - clearevents(); - return mp_const_none; +STATIC mp_obj_t modgint_clearevents(void) +{ + clearevents(); + return mp_const_none; } -STATIC mp_obj_t modgint_cleareventflips(void) { - cleareventflips(); - return mp_const_none; +STATIC mp_obj_t modgint_cleareventflips(void) +{ + cleareventflips(); + return mp_const_none; } -STATIC mp_obj_t modgint_keydown(mp_obj_t arg1) { - mp_int_t key = mp_obj_get_int(arg1); - bool down = keydown(key) != 0; - return mp_obj_new_bool(down); +STATIC mp_obj_t modgint_keydown(mp_obj_t arg1) +{ + mp_int_t key = mp_obj_get_int(arg1); + bool down = keydown(key) != 0; + return mp_obj_new_bool(down); } -STATIC mp_obj_t modgint_keydown_all(size_t n, mp_obj_t const *args) { - bool down = true; - for (size_t i = 0; i < n; i++) - down &= keydown(mp_obj_get_int(args[i])) != 0; - return mp_obj_new_bool(down); +STATIC mp_obj_t modgint_keydown_all(size_t n, mp_obj_t const *args) +{ + bool down = true; + for(size_t i = 0; i < n; i++) + down &= keydown(mp_obj_get_int(args[i])) != 0; + return mp_obj_new_bool(down); } -STATIC mp_obj_t modgint_keydown_any(size_t n, mp_obj_t const *args) { - bool down = false; - for (size_t i = 0; i < n; i++) - down |= keydown(mp_obj_get_int(args[i])) != 0; - return mp_obj_new_bool(down); +STATIC mp_obj_t modgint_keydown_any(size_t n, mp_obj_t const *args) +{ + bool down = false; + for(size_t i = 0; i < n; i++) + down |= keydown(mp_obj_get_int(args[i])) != 0; + return mp_obj_new_bool(down); } -STATIC mp_obj_t modgint_keypressed(mp_obj_t arg1) { - mp_int_t key = mp_obj_get_int(arg1); - return mp_obj_new_bool(keypressed(key) != 0); +STATIC mp_obj_t modgint_keypressed(mp_obj_t arg1) +{ + mp_int_t key = mp_obj_get_int(arg1); + return mp_obj_new_bool(keypressed(key) != 0); } -STATIC mp_obj_t modgint_keyreleased(mp_obj_t arg1) { - mp_int_t key = mp_obj_get_int(arg1); - return mp_obj_new_bool(keyreleased(key) != 0); +STATIC mp_obj_t modgint_keyreleased(mp_obj_t arg1) +{ + mp_int_t key = mp_obj_get_int(arg1); + return mp_obj_new_bool(keyreleased(key) != 0); } -STATIC mp_obj_t modgint_getkey(void) { - key_event_t ev = getkey(); - return mk_key_event(ev); +STATIC mp_obj_t modgint_getkey(void) +{ + key_event_t ev = getkey(); + return mk_key_event(ev); } // TODO: getkey_opt: timeout parameter? -STATIC mp_obj_t modgint_getkey_opt(mp_obj_t arg1) { - int options = mp_obj_get_int(arg1); - key_event_t ev = getkey_opt(options, NULL); - return mk_key_event(ev); +STATIC mp_obj_t modgint_getkey_opt(mp_obj_t arg1) +{ + int options = mp_obj_get_int(arg1); + key_event_t ev = getkey_opt(options, NULL); + return mk_key_event(ev); } -STATIC mp_obj_t modgint_keycode_function(mp_obj_t arg1) { - int keycode = mp_obj_get_int(arg1); - return MP_OBJ_NEW_SMALL_INT(keycode_function(keycode)); +STATIC mp_obj_t modgint_keycode_function(mp_obj_t arg1) +{ + int keycode = mp_obj_get_int(arg1); + return MP_OBJ_NEW_SMALL_INT(keycode_function(keycode)); } -STATIC mp_obj_t modgint_keycode_digit(mp_obj_t arg1) { - int keycode = mp_obj_get_int(arg1); - return MP_OBJ_NEW_SMALL_INT(keycode_digit(keycode)); +STATIC mp_obj_t modgint_keycode_digit(mp_obj_t arg1) +{ + int keycode = mp_obj_get_int(arg1); + return MP_OBJ_NEW_SMALL_INT(keycode_digit(keycode)); } FUN_0(clearevents); @@ -134,168 +152,152 @@ FUN_VAR(keydown_any, 0); FUN_1(keypressed); FUN_1(keyreleased); FUN_0(getkey); -FUN_1 /*2*/ (getkey_opt); +FUN_1/*2*/(getkey_opt); FUN_1(keycode_function); FUN_1(keycode_digit); /* */ #ifdef FXCG50 -STATIC mp_obj_t modgint_C_RGB(mp_obj_t arg1, mp_obj_t arg2, mp_obj_t arg3) { - mp_int_t r = mp_obj_get_int(arg1); - mp_int_t g = mp_obj_get_int(arg2); - mp_int_t b = mp_obj_get_int(arg3); - return MP_OBJ_NEW_SMALL_INT(C_RGB(r, g, b)); +STATIC mp_obj_t modgint_C_RGB(mp_obj_t arg1, mp_obj_t arg2, mp_obj_t arg3) +{ + mp_int_t r = mp_obj_get_int(arg1); + mp_int_t g = mp_obj_get_int(arg2); + mp_int_t b = mp_obj_get_int(arg3); + return MP_OBJ_NEW_SMALL_INT(C_RGB(r, g, b)); } #endif -STATIC mp_obj_t modgint_dclear(mp_obj_t arg1) { - mp_int_t color = mp_obj_get_int(arg1); - dclear(color); - return mp_const_none; -} - -STATIC mp_obj_t modgint_dupdate(void) { - pe_enter_graphics_mode(); - dupdate(); - return mp_const_none; -} - -STATIC mp_obj_t modgint_drect(size_t n, mp_obj_t const *args) { - mp_int_t x1 = mp_obj_get_int(args[0]); - mp_int_t y1 = mp_obj_get_int(args[1]); - mp_int_t x2 = mp_obj_get_int(args[2]); - mp_int_t y2 = mp_obj_get_int(args[3]); - mp_int_t color = mp_obj_get_int(args[4]); - drect(x1, y1, x2, y2, color); - return mp_const_none; -} - -STATIC mp_obj_t modgint_drect_border(size_t n, mp_obj_t const *args) { - mp_int_t x1 = mp_obj_get_int(args[0]); - mp_int_t y1 = mp_obj_get_int(args[1]); - mp_int_t x2 = mp_obj_get_int(args[2]); - mp_int_t y2 = mp_obj_get_int(args[3]); - mp_int_t fill_color = mp_obj_get_int(args[4]); - mp_int_t border_width = mp_obj_get_int(args[5]); - mp_int_t border_color = mp_obj_get_int(args[6]); - drect_border(x1, y1, x2, y2, fill_color, border_width, border_color); - return mp_const_none; -} - -STATIC mp_obj_t modgint_dpixel(mp_obj_t arg1, mp_obj_t arg2, mp_obj_t arg3) { - mp_int_t x = mp_obj_get_int(arg1); - mp_int_t y = mp_obj_get_int(arg2); - mp_int_t color = mp_obj_get_int(arg3); - dpixel(x, y, color); - return mp_const_none; -} - -STATIC mp_obj_t modgint_dgetpixel(mp_obj_t arg1, mp_obj_t arg2) { - mp_int_t x = mp_obj_get_int(arg1); - mp_int_t y = mp_obj_get_int(arg2); - return MP_OBJ_NEW_SMALL_INT(dgetpixel(x, y)); -} - -STATIC mp_obj_t modgint_dline(size_t n, mp_obj_t const *args) { - mp_int_t x1 = mp_obj_get_int(args[0]); - mp_int_t y1 = mp_obj_get_int(args[1]); - mp_int_t x2 = mp_obj_get_int(args[2]); - mp_int_t y2 = mp_obj_get_int(args[3]); - mp_int_t color = mp_obj_get_int(args[4]); - dline(x1, y1, x2, y2, color); - return mp_const_none; -} - -STATIC mp_obj_t modgint_dhline(mp_obj_t arg1, mp_obj_t arg2) { - mp_int_t y = mp_obj_get_int(arg1); - mp_int_t color = mp_obj_get_int(arg2); - dhline(y, color); - return mp_const_none; -} - -STATIC mp_obj_t modgint_dvline(mp_obj_t arg1, mp_obj_t arg2) { - mp_int_t x = mp_obj_get_int(arg1); - mp_int_t color = mp_obj_get_int(arg2); - dvline(x, color); - return mp_const_none; -} - -STATIC mp_obj_t modgint_dpoly(mp_obj_t points, mp_obj_t fill, mp_obj_t border) { - - mp_uint_t nbitems; - mp_obj_t *items; - - mp_obj_list_get(points, &nbitems, &items); - - unsigned int len = nbitems / 2; - int *x = (int *)malloc(len * sizeof(int)); - int *y = (int *)malloc(len * sizeof(int)); - - if (!x || !y) +STATIC mp_obj_t modgint_dclear(mp_obj_t arg1) +{ + mp_int_t color = mp_obj_get_int(arg1); + dclear(color); return mp_const_none; - - for (unsigned int u = 0; u < len; u++) { - x[u] = mp_obj_get_int(items[u * 2]); - y[u] = mp_obj_get_int(items[u * 2 + 1]); - } - - mp_int_t fillcolor = mp_obj_get_int(fill); - mp_int_t bordercolor = mp_obj_get_int(border); - - dpoly(x, y, len, fillcolor, bordercolor); - - free(x); - free(y); - - return mp_const_none; } -STATIC mp_obj_t modgint_dcircle(size_t n_args, const mp_obj_t *args) { - mp_int_t x = mp_obj_get_int(args[0]); - mp_int_t y = mp_obj_get_int(args[1]); - mp_int_t r = mp_obj_get_int(args[2]); - mp_int_t fill = mp_obj_get_int(args[3]); - mp_int_t border = mp_obj_get_int(args[4]); - - dcircle(x, y, r, fill, border); - return mp_const_none; +STATIC mp_obj_t modgint_dupdate(void) +{ + pe_enter_graphics_mode(); + dupdate(); + return mp_const_none; } -STATIC mp_obj_t modgint_dellipse(size_t n_args, const mp_obj_t *args) { - mp_int_t x1 = mp_obj_get_int(args[0]); - mp_int_t y1 = mp_obj_get_int(args[1]); - mp_int_t x2 = mp_obj_get_int(args[2]); - mp_int_t y2 = mp_obj_get_int(args[3]); - mp_int_t fill = mp_obj_get_int(args[4]); - mp_int_t border = mp_obj_get_int(args[5]); +STATIC mp_obj_t modgint_drect(size_t n, mp_obj_t const *args) +{ + mp_int_t x1 = mp_obj_get_int(args[0]); + mp_int_t y1 = mp_obj_get_int(args[1]); + mp_int_t x2 = mp_obj_get_int(args[2]); + mp_int_t y2 = mp_obj_get_int(args[3]); + mp_int_t color = mp_obj_get_int(args[4]); + drect(x1, y1, x2, y2, color); + return mp_const_none; +} - dellipse(x1, y1, x2, y2, fill, border); - return mp_const_none; +STATIC mp_obj_t modgint_drect_border(size_t n, mp_obj_t const *args) +{ + mp_int_t x1 = mp_obj_get_int(args[0]); + mp_int_t y1 = mp_obj_get_int(args[1]); + mp_int_t x2 = mp_obj_get_int(args[2]); + mp_int_t y2 = mp_obj_get_int(args[3]); + mp_int_t fill_color = mp_obj_get_int(args[4]); + mp_int_t border_width = mp_obj_get_int(args[5]); + mp_int_t border_color = mp_obj_get_int(args[6]); + drect_border(x1, y1, x2, y2, fill_color, border_width, border_color); + return mp_const_none; +} + +STATIC mp_obj_t modgint_dpixel(mp_obj_t arg1, mp_obj_t arg2, mp_obj_t arg3) +{ + mp_int_t x = mp_obj_get_int(arg1); + mp_int_t y = mp_obj_get_int(arg2); + mp_int_t color = mp_obj_get_int(arg3); + dpixel(x, y, color); + return mp_const_none; +} + +STATIC mp_obj_t modgint_dgetpixel(mp_obj_t arg1, mp_obj_t arg2) +{ + mp_int_t x = mp_obj_get_int(arg1); + mp_int_t y = mp_obj_get_int(arg2); + return MP_OBJ_NEW_SMALL_INT(dgetpixel(x, y)); +} + +STATIC mp_obj_t modgint_dline(size_t n, mp_obj_t const *args) +{ + mp_int_t x1 = mp_obj_get_int(args[0]); + mp_int_t y1 = mp_obj_get_int(args[1]); + mp_int_t x2 = mp_obj_get_int(args[2]); + mp_int_t y2 = mp_obj_get_int(args[3]); + mp_int_t color = mp_obj_get_int(args[4]); + dline(x1, y1, x2, y2, color); + return mp_const_none; +} + +STATIC mp_obj_t modgint_dhline(mp_obj_t arg1, mp_obj_t arg2) +{ + mp_int_t y = mp_obj_get_int(arg1); + mp_int_t color = mp_obj_get_int(arg2); + dhline(y, color); + return mp_const_none; +} + +STATIC mp_obj_t modgint_dvline(mp_obj_t arg1, mp_obj_t arg2) +{ + mp_int_t x = mp_obj_get_int(arg1); + mp_int_t color = mp_obj_get_int(arg2); + dvline(x, color); + return mp_const_none; +} + +STATIC mp_obj_t modgint_dcircle(size_t n_args, const mp_obj_t *args) +{ + mp_int_t x = mp_obj_get_int(args[0]); + mp_int_t y = mp_obj_get_int(args[1]); + mp_int_t r = mp_obj_get_int(args[2]); + mp_int_t fill = mp_obj_get_int(args[3]); + mp_int_t border = mp_obj_get_int(args[4]); + + dcircle(x, y, r, fill, border); + return mp_const_none; +} + +STATIC mp_obj_t modgint_dellipse(size_t n_args, const mp_obj_t *args) +{ + mp_int_t x1 = mp_obj_get_int(args[0]); + mp_int_t y1 = mp_obj_get_int(args[1]); + mp_int_t x2 = mp_obj_get_int(args[2]); + mp_int_t y2 = mp_obj_get_int(args[3]); + mp_int_t fill = mp_obj_get_int(args[4]); + mp_int_t border = mp_obj_get_int(args[5]); + + dellipse(x1, y1, x2, y2, fill, border); + return mp_const_none; } // TODO: modgint: Font management? -STATIC mp_obj_t modgint_dtext_opt(size_t n, mp_obj_t const *args) { - mp_int_t x = mp_obj_get_int(args[0]); - mp_int_t y = mp_obj_get_int(args[1]); - mp_int_t fg = mp_obj_get_int(args[2]); - mp_int_t bg = mp_obj_get_int(args[3]); - mp_int_t halign = mp_obj_get_int(args[4]); - mp_int_t valign = mp_obj_get_int(args[5]); - char const *str = mp_obj_str_get_str(args[6]); - mp_int_t size = mp_obj_get_int(args[7]); - dtext_opt(x, y, fg, bg, halign, valign, str, size); - return mp_const_none; +STATIC mp_obj_t modgint_dtext_opt(size_t n, mp_obj_t const *args) +{ + mp_int_t x = mp_obj_get_int(args[0]); + mp_int_t y = mp_obj_get_int(args[1]); + mp_int_t fg = mp_obj_get_int(args[2]); + mp_int_t bg = mp_obj_get_int(args[3]); + mp_int_t halign = mp_obj_get_int(args[4]); + mp_int_t valign = mp_obj_get_int(args[5]); + char const *str = mp_obj_str_get_str(args[6]); + mp_int_t size = mp_obj_get_int(args[7]); + dtext_opt(x, y, fg, bg, halign, valign, str, size); + return mp_const_none; } -STATIC mp_obj_t modgint_dtext(size_t n, mp_obj_t const *args) { - mp_int_t x = mp_obj_get_int(args[0]); - mp_int_t y = mp_obj_get_int(args[1]); - mp_int_t fg = mp_obj_get_int(args[2]); - char const *str = mp_obj_str_get_str(args[3]); - dtext(x, y, fg, str); - return mp_const_none; +STATIC mp_obj_t modgint_dtext(size_t n, mp_obj_t const *args) +{ + mp_int_t x = mp_obj_get_int(args[0]); + mp_int_t y = mp_obj_get_int(args[1]); + mp_int_t fg = mp_obj_get_int(args[2]); + char const *str = mp_obj_str_get_str(args[3]); + dtext(x, y, fg, str); + return mp_const_none; } FUN_0(__init__); @@ -312,7 +314,6 @@ FUN_2(dgetpixel); FUN_BETWEEN(dline, 5, 5); FUN_2(dhline); FUN_2(dvline); -FUN_3(dpoly); FUN_BETWEEN(dcircle, 5, 5); FUN_BETWEEN(dellipse, 6, 6); FUN_BETWEEN(dtext_opt, 8, 8); @@ -321,15 +322,13 @@ FUN_BETWEEN(dtext, 4, 4); /* Module definition */ // Helper: define object "modgint_F_obj" as object "F" in the module -#define OBJ(F) \ - { MP_ROM_QSTR(MP_QSTR_##F), MP_ROM_PTR(&modgint_##F##_obj) } +#define OBJ(F) {MP_ROM_QSTR(MP_QSTR_ ## F), MP_ROM_PTR(&modgint_ ## F ## _obj)} // Helper: define small integer constant "I" as "I" in the module -#define INT(I) \ - { MP_ROM_QSTR(MP_QSTR_##I), MP_OBJ_NEW_SMALL_INT(I) } +#define INT(I) {MP_ROM_QSTR(MP_QSTR_ ## I), MP_OBJ_NEW_SMALL_INT(I)} STATIC const mp_rom_map_elem_t modgint_module_globals_table[] = { - {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_gint)}, + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_gint) }, OBJ(__init__), /* */ @@ -470,17 +469,16 @@ STATIC const mp_rom_map_elem_t modgint_module_globals_table[] = { OBJ(dline), OBJ(dhline), OBJ(dvline), - OBJ(dpoly), OBJ(dcircle), OBJ(dellipse), OBJ(dtext_opt), OBJ(dtext), }; -STATIC MP_DEFINE_CONST_DICT(modgint_module_globals, - modgint_module_globals_table); +STATIC MP_DEFINE_CONST_DICT( + modgint_module_globals, modgint_module_globals_table); const mp_obj_module_t modgint_module = { - .base = {&mp_type_module}, + .base = { &mp_type_module }, .globals = (mp_obj_dict_t *)&modgint_module_globals, }; From 8072e5b1a9b316dd8467f33814e5efd52c3cf656 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Fri, 2 Feb 2024 22:16:56 +0100 Subject: [PATCH 10/27] added monotonic() function in NW time module --- ports/sh/numworks/time.c | 54 ++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/ports/sh/numworks/time.c b/ports/sh/numworks/time.c index 7e26c16d0..bd8277d61 100644 --- a/ports/sh/numworks/time.c +++ b/ports/sh/numworks/time.c @@ -13,6 +13,7 @@ #include "py/objtuple.h" #include "py/runtime.h" #include +#include #include #include @@ -27,25 +28,60 @@ #define FUN_BETWEEN(NAME, MIN, MAX) \ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(time_##NAME##_obj, MIN, MAX, time_##NAME) -STATIC mp_obj_t time___init__(void) { return mp_const_none; } + + +extern bool is_timered; +extern unsigned int timer_altered[9]; + +static uint64_t tickmono = 0; + + +static int monotonic_callback(void) { + tickmono++; + return TIMER_CONTINUE; +} + + +STATIC mp_obj_t time___init__(void) +{ + int t = timer_configure(TIMER_TMU, 1000, GINT_CALL(monotonic_callback)); + if (t >= 0) + { + timer_start(t); + is_timered = true; // there is a timer altered from this module + timer_altered[t] = 1; // we put the corresponding timer at 1 to identify it + } + + return mp_const_none; +} /* */ -STATIC mp_obj_t time_sleep(mp_obj_t arg1) { - mp_float_t duration = mp_obj_get_float(arg1); +STATIC mp_obj_t time_sleep(mp_obj_t arg1) +{ + mp_float_t duration = mp_obj_get_float(arg1); - uint64_t length = - (uint64_t)(duration * - 1000000.0f); // duration is in seconds and length in µs + uint64_t length = (uint64_t)(duration * 1000000.0f); // duration is in seconds and length in µs - sleep_us(length); + sleep_us(length); - return mp_const_none; + return mp_const_none; } +STATIC mp_obj_t time_monotonic(void) +{ + float value = (float) ((uint64_t) (tickmono * 1000 +0.5 )) / 1000000.0f; + + return mp_obj_new_float( value ); +} + + + FUN_1(sleep); +FUN_0(monotonic); FUN_0(__init__); + /* Module definittime */ // Helper: define object "time_F_obj" as object "F" in the module @@ -60,7 +96,9 @@ STATIC const mp_rom_map_elem_t time_module_globals_table[] = { {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_time)}, OBJ(__init__), OBJ(sleep), + OBJ(monotonic), }; + STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); const mp_obj_module_t time_module = { From 0fb961768a28832ff16ebdf6b8e8f50022954393 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Sat, 3 Feb 2024 09:05:00 +0100 Subject: [PATCH 11/27] Added examples in ports/sh/examples + documentation in docs/sh --- docs/sh/NWcompat_modules.md | 101 +++++++++++++++++++++++++ ports/sh/examples/cg_NW_kand_mandel.py | 27 +++++++ ports/sh/examples/cg_NW_kand_test.py | 21 +++++ ports/sh/examples/cg_NW_rosace.py | 77 +++++++++++++++++++ ports/sh/examples/cg_NW_time_test.py | 15 ++++ ports/sh/numworks/time.c | 2 + 6 files changed, 243 insertions(+) create mode 100644 docs/sh/NWcompat_modules.md create mode 100644 ports/sh/examples/cg_NW_kand_mandel.py create mode 100644 ports/sh/examples/cg_NW_kand_test.py create mode 100644 ports/sh/examples/cg_NW_rosace.py create mode 100644 ports/sh/examples/cg_NW_time_test.py diff --git a/docs/sh/NWcompat_modules.md b/docs/sh/NWcompat_modules.md new file mode 100644 index 000000000..41ef5ea87 --- /dev/null +++ b/docs/sh/NWcompat_modules.md @@ -0,0 +1,101 @@ +# utilisation des modules `kandinsky`, `ion` et `time` de l'implémentation Python sur `NumWorks` + +PythonExtra offre la possibilité d'utiliser certains modules de la Numworks afin de rendre les scripts de cette machine compatible en l'état sur la fxCG50 (pas de support sur la fx9860G pour cause de mémoire insuffisante). + +Il s'agit d'un Work in Progress (WIP) et le support est à ce stade partiel, néanmoins les modules `kandinsky`, `ion` et `time` de la NumWorks sont supportés. + +## `kandinsky` + +Le module `kandinsky` offre le support des primitives graphiques via les routines hautes performance de `gint`. Toutes les fonctions de ce module sont disponibles : + +- `color(r,g,b)` : Génère la valeur de la couleur r,g,b. Vous pouvez aussi simplement utiliser un tuple pour définir une couleur : (r,g,b). + +- `get_pixel(x,y)` : Renvoie la couleur du pixel aux coordonnées x,y sous forme de tuple (r,g,b). + +- `set_pixel(x,y,color)` : Allume le pixel x,y de la couleur color. + +- `draw_string(text,x,y,[color1],[color2])` : Affiche le texte text aux coordonnées x,y. Les arguments color1 (couleur du texte) et color2 (couleur de lʼarrière plan du texte) sont optionnels. + +- `fill_rect(x,y,width,height,color)` : Remplit un rectangle de largeur width et de hauteur height avec la couleur color au point de coordonnées x et y. + +Le module offre de plus un certain nombre de couleurs explicitement nommées et accessibles par une chaine de caractères. Les valeurs suivantes sont utilisables en lieu et place des paramètres de couleur des fonctions de `kandinsky` : +- "red", "r" +- "green", "g" +- "blue", "b" +- "black", "k" +- "white", "w" +- "yellow", "y" +- "pink" +- "magenta" +- "grey", "gray" +- "purple" +- "orange" +- "cyan" +- "brown" + + +## `ion` + +Le module `ion` donne accès à la fonction `keydown(k)` qui renvoie True si la touche k placée en argument est appuyée et False sinon. + +La "conversion" des touches entre la machine NumWorks et Casio fxCG50 se fait selon le mapping suivant : + +| NumWorks | Casio fxCG50 | +|----------|--------------| +| KEY_LEFT | KEY_LEFT | +| KEY_UP | KEY_UP | +| KEY_DOWN | KEY_DOWN | +| KEY_RIGHT | KEY_RIGHT | +| KEY_OK | KEY_F1 | +| KEY_BACK | KEY_EXIT | +| KEY_HOME | KEY_MENU | +| KEY_ONOFF | KEY_ACON | +| KEY_SHIFT | KEY_SHIFT | +| KEY_ALPHA | KEY_ALPHA | +| KEY_XNT | KEY_XOT | +| KEY_VAR | KEY_VARS | +| KEY_TOOLBOX | KEY_OPTN | +| KEY_BACKSPACE | KEY_DEL | +| KEY_EXP | KEY_EXP | +| KEY_LN | KEY_LN | +| KEY_LOG | KEY_LOG | +| KEY_IMAGINARY | KEY_F2 | +| KEY_COMMA | KEY_COMMA | +| KEY_POWER | KEY_POWER | +| KEY_SINE | KEY_SIN | +| KEY_COSINE | KEY_COS | +| KEY_TANGENT | KEY_TAN | +| KEY_PI | KEY_F3 | +| KEY_SQRT | KEY_F4 | +| KEY_SQUARE | KEY_SQUARE | +| KEY_SEVEN | KEY_7 | +| KEY_EIGHT | KEY_8 | +| KEY_NINE | KEY_9 | +| KEY_LEFTPARENTHESIS | KEY_LEFTP | +| KEY_RIGHTPARENTHESIS | KEY_RIGHTP | +| KEY_FOUR | KEY_4 | +| KEY_FIVE | KEY_5 | +| KEY_SIX | KEY_6 | +| KEY_MULTIPLICATION | KEY_MUL | +| KEY_DIVISION | KEY_DIV | +| KEY_ONE | KEY_1 | +| KEY_TWO | KEY_2 | +| KEY_THREE | KEY_3 | +| KEY_PLUS | KEY_ADD | +| KEY_MINUS | KEY_SUB | +| KEY_ZERO | KEY_0 | +| KEY_DOT | KEY_DOT | +| KEY_EE | KEY_F5 | +| KEY_ANS | KEY_NEG | +| KEY_EXE | KEY_EXE | + + +## `time` + +Le module `time` donne accès à deux fonctions : + +- `monotonic()` : Renvoie la valeur de lʼhorloge au moment où la fonction est appelée. + +- `sleep(t)` : Suspend lʼexécution pendant t secondes. + + diff --git a/ports/sh/examples/cg_NW_kand_mandel.py b/ports/sh/examples/cg_NW_kand_mandel.py new file mode 100644 index 000000000..8148dca9f --- /dev/null +++ b/ports/sh/examples/cg_NW_kand_mandel.py @@ -0,0 +1,27 @@ +# This script draws a Mandelbrot fractal set +# N_iteration: degree of precision +import kandinsky +import ion +import time + +N_iteration = 10 + +for x in range(320): + for y in range(222): +# Compute the mandelbrot sequence for the point c = (c_r, c_i) with start value z = (z_r, z_i) + z = complex(0,0) +# Rescale to fit the drawing screen 320x222 + c = complex(3.5*x/319-2.5, -2.5*y/221+1.25) + i = 0 + while (i < N_iteration) and abs(z) < 2: + i = i + 1 + z = z*z+c +# Choose the color of the dot from the Mandelbrot sequence + rgb = int(255*i/N_iteration) + col = kandinsky.color(int(rgb),int(rgb*0.75),int(rgb*0.25)) +# Draw a pixel colored in 'col' at position (x,y) + kandinsky.set_pixel(x,y,col) + +while not ion.keydown(ion.KEY_EXE): + time.sleep(0.1) + diff --git a/ports/sh/examples/cg_NW_kand_test.py b/ports/sh/examples/cg_NW_kand_test.py new file mode 100644 index 000000000..b1eea55e6 --- /dev/null +++ b/ports/sh/examples/cg_NW_kand_test.py @@ -0,0 +1,21 @@ +from kandinsky import * +from gint import * + +draw_string( "Hello Kandinsky", 10, 10, "white", "red" ) + +draw_string( "Hello Kandinsky", 10, 200, "k" ) + +fill_rect( 25, 25, 100, 100, 00000 ) +fill_rect( 60, 25, 10, 100, 65000 ) +fill_rect( 25, 60, 100, 10, 00031 ) + +fill_rect( 100, 100, 25, 25, "green" ) + +fill_rect( 200, 100, 25, 25, (255,255,0) ) + +fill_rect( 200, 50, 25, 25, (128,0,255) ) + +set_pixel( 150, 150, "red" ) +set_pixel( 160, 160, (0,0,255) ) + +getkey() diff --git a/ports/sh/examples/cg_NW_rosace.py b/ports/sh/examples/cg_NW_rosace.py new file mode 100644 index 000000000..42e6b4aa9 --- /dev/null +++ b/ports/sh/examples/cg_NW_rosace.py @@ -0,0 +1,77 @@ +from kandinsky import * +from math import * +import ion +import time + +def cercle1(x0,y0,r,c,e): + for i in range(2*e): + xd=x0-int((r-i*0.5)/sqrt(2)) + xf=x0+int((r-i*0.5)/sqrt(2)) + for x in range(xd,xf+1): + x1=x + y1=y0+int(sqrt((r-i*0.5)**2-(x-x0)**2)) + if sqrt((160-x1)**2+(111-y1)**2)n-4:col=c2 + else:col=c1 + cercle2(x1,y1,rj,col,e) + + +col1=color(5,50,120) +col2=color(255,45,45) +col3=color(245,225,25) +#rosace1(12,50,col1,2) +#rosace2(12,50,col1,1) +rosace3(10,55,col1,col2,col3,1) + +while not ion.keydown(ion.KEY_EXE): + time.sleep(0.1) \ No newline at end of file diff --git a/ports/sh/examples/cg_NW_time_test.py b/ports/sh/examples/cg_NW_time_test.py new file mode 100644 index 000000000..e94b85850 --- /dev/null +++ b/ports/sh/examples/cg_NW_time_test.py @@ -0,0 +1,15 @@ +from time import * + +print(monotonic()) + +sleep(1) + +print(monotonic()) + +sleep(2) + +print(monotonic()) + +sleep(1) + +print(monotonic()) diff --git a/ports/sh/numworks/time.c b/ports/sh/numworks/time.c index bd8277d61..debcdd0ec 100644 --- a/ports/sh/numworks/time.c +++ b/ports/sh/numworks/time.c @@ -44,6 +44,8 @@ static int monotonic_callback(void) { STATIC mp_obj_t time___init__(void) { + tickmono = 0; + int t = timer_configure(TIMER_TMU, 1000, GINT_CALL(monotonic_callback)); if (t >= 0) { From e7ad1e267d03643bdfda7b82ee0ca54130ff205b Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Sat, 3 Feb 2024 18:09:48 +0100 Subject: [PATCH 12/27] Add extra memory for fxCG50 hardware and fxCG emulator - todo for fxCG10/20 --- ports/sh/main.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/ports/sh/main.c b/ports/sh/main.c index e63b5fcfb..922d62bcc 100644 --- a/ports/sh/main.c +++ b/ports/sh/main.c @@ -403,6 +403,36 @@ int main(int argc, char **argv) - The OS' extra VRAM - Memory past the 2 MB boundary on tested OSes */ // gc_add(start, end)... + + /* TODO : test to check if we can definitely maintain this addition of RAM */ + + if (gint[HWCALC] == HWCALC_FXCG50) + { + char const *osv = (char*) 0x80020020; + if((!strncmp(osv, "03.", 3) && osv[3] <= '8')) // CG-50 + { + void *py_ram_start = (void *)0x8c200000; + void *py_ram_end = (void *)0x8c4e0000; + gc_add(py_ram_start, py_ram_end); + } + } + +/* + // TODO : Add more RAM for the PRIZM + + else if (gint[HWCALC] == HWCALC_PRIZM) // CG-10/20 + { + + } +*/ + else if (gint[HWCALC] == HWCALC_FXCG_MANAGER) // CG-50 EMULATOR + { + void *py_ram_start = (void *)0x88200000; + void *py_ram_end = (void *)0x884e0000; + gc_add(py_ram_start, py_ram_end); + } + + #endif mp_init(); From 6ae6d063877f5eab203a756d2a984c6e72f4e888 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Sat, 3 Feb 2024 20:11:17 +0100 Subject: [PATCH 13/27] Additional RAM - but a bit less greedy in the approach ;) --- ports/sh/main.c | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/ports/sh/main.c b/ports/sh/main.c index 922d62bcc..b1c0c85ec 100644 --- a/ports/sh/main.c +++ b/ports/sh/main.c @@ -406,32 +406,9 @@ int main(int argc, char **argv) /* TODO : test to check if we can definitely maintain this addition of RAM */ - if (gint[HWCALC] == HWCALC_FXCG50) - { - char const *osv = (char*) 0x80020020; - if((!strncmp(osv, "03.", 3) && osv[3] <= '8')) // CG-50 - { - void *py_ram_start = (void *)0x8c200000; - void *py_ram_end = (void *)0x8c4e0000; - gc_add(py_ram_start, py_ram_end); - } - } - -/* - // TODO : Add more RAM for the PRIZM - - else if (gint[HWCALC] == HWCALC_PRIZM) // CG-10/20 - { - - } -*/ - else if (gint[HWCALC] == HWCALC_FXCG_MANAGER) // CG-50 EMULATOR - { - void *py_ram_start = (void *)0x88200000; - void *py_ram_end = (void *)0x884e0000; - gc_add(py_ram_start, py_ram_end); - } - + void *uram_area = kmalloc(300000, "_uram"); + if(uram_area) + gc_add(uram_area, uram_area+300000); #endif From ba515dc168238073367c9c2097d441677dcd7b0b Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Sun, 4 Feb 2024 16:00:40 +0100 Subject: [PATCH 14/27] Add Kandinsky Extension for fxCG to be able to use wide screen --- docs/sh/NWcompat_modules.md | 12 +++++++++ ports/sh/examples/cg_NW_kand_test.py | 25 ++++++++++++++++++ ports/sh/numworks/modkandinsky.c | 38 ++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/docs/sh/NWcompat_modules.md b/docs/sh/NWcompat_modules.md index 41ef5ea87..c3151d7b9 100644 --- a/docs/sh/NWcompat_modules.md +++ b/docs/sh/NWcompat_modules.md @@ -33,6 +33,18 @@ Le module offre de plus un certain nombre de couleurs explicitement nommées et - "cyan" - "brown" +Les fonctions suivantes sont des ajouts pour tirer partie de l'écran large de la fxCG et qui sont donc une extension du module `Kandinsky`. Elles ne sont donc par définition pas compatible avec le Python Numwork. Ces fonctions sont reconnaisables à leurs appellations qui commencent toutes par `CGEXT_` : + +- `CGEXT_Enable_Wide_Screen()` : Active l'écran étendu de la fxCG, aucun paramètre n'est nécessaire. Les coordonnées x de l'écran physique peuvent être négatives pour empiéter sur la bande blanche de gauche et supérieures à 319 pixels pour empièter sur la bande blanche de droite; + +- `CGEXT_Disable_Wide_Screen()` : Annule l'activation de l'écran étendu de la fxCG, aucun paramètre n'est nécessaire. Les coordonnées x de l'écran physique seront contraintes entre 0 et 320 pixels. Au-delà, le tracé ne sera pas effectué. + +- `CGEXT_Is_Wide_Screen_Enabled()` : Retourne `True` si l'écran étendu est actif et `False` dans le cas contraire. + +Note 1 : après avoir réalisé un tracé dans la zone étendue, il faut que celle-ci soit active pour permettre son effacement (typiquement via un appel à la fonction `fill_rect()` avec les paramètres adéquats). + +Note 2 : En mode non étendu (par défaut à l'initialisation du module `Kandinsky`) les coordonnées de l'écran vont de (0,0) à (319,239) (avec une partie visible allant de (0,0) à (319,223) du fait de la hauteur réduite de l'écran de la fxCG). En mode étendu, les coordonnées de l'écran vont de (-38,0) à (358,239) (avec une partie visible allant de (-38,0) à (358,223) du fait de la hauteur réduite de l'écran de la fxCG) + ## `ion` diff --git a/ports/sh/examples/cg_NW_kand_test.py b/ports/sh/examples/cg_NW_kand_test.py index b1eea55e6..8b2c9681c 100644 --- a/ports/sh/examples/cg_NW_kand_test.py +++ b/ports/sh/examples/cg_NW_kand_test.py @@ -18,4 +18,29 @@ fill_rect( 200, 50, 25, 25, (128,0,255) ) set_pixel( 150, 150, "red" ) set_pixel( 160, 160, (0,0,255) ) + +# The following functions are only valid on fxCG (this is an extension of Kandinsky to take benefit of wide screen + +color = "black" + +if CGEXT_Is_Wide_Screen_Enabled() : + color = "red" +fill_rect( -100, 150, 500, 10, color ) + +CGEXT_Enable_Wide_Screen() + +if CGEXT_Is_Wide_Screen_Enabled() : + color = "green" +else : + color = "purple" +fill_rect( -100, 165, 500, 10, color ) + +CGEXT_Disable_Wide_Screen() + +if CGEXT_Is_Wide_Screen_Enabled() : + color = "blue" +else : + color = "red" +fill_rect( -100, 180, 500, 10, color ) + getkey() diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index 18a4cc7f6..7cada6072 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -233,6 +233,41 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { return mp_const_none; } +static mp_obj_t Kandinsky_CGEXT_Enable_Wide_Screen( void ) { + + struct dwindow nw; + nw.left = 0; + nw.top = 0; + nw.right = DWIDTH; + nw.bottom = DHEIGHT; + dwindow_set(nw); + is_dwindowed = false; // we mark as not windowed + + return mp_const_none; +} + +static mp_obj_t Kandinsky_CGEXT_Disable_Wide_Screen( void ) { + + struct dwindow nw; + nw.left = DELTAXNW; + nw.top = DELTAYNW; + nw.right = 320 + DELTAXNW; + nw.bottom = 240 + DELTAYNW; + dwindow_set(nw); + is_dwindowed = true; // we mark as windowed + + return mp_const_none; +} + +static mp_obj_t Kandinsky_CGEXT_Is_Wide_Screen_Enabled( void ) { + + return mp_obj_new_bool( is_dwindowed ); +} + +MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_CGEXT_Enable_Wide_Screen_obj, Kandinsky_CGEXT_Enable_Wide_Screen); +MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_CGEXT_Disable_Wide_Screen_obj, Kandinsky_CGEXT_Disable_Wide_Screen); +MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_CGEXT_Is_Wide_Screen_Enabled_obj, Kandinsky_CGEXT_Is_Wide_Screen_Enabled); + MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_init_obj, Kandinsky_init); MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_set_pixel_obj, 3, 3, Kandinsky_set_pixel); @@ -251,6 +286,9 @@ STATIC const mp_rom_map_elem_t kandinsky_module_globals_table[] = { {MP_ROM_QSTR(MP_QSTR_set_pixel), MP_ROM_PTR(&Kandinsky_set_pixel_obj)}, {MP_ROM_QSTR(MP_QSTR_get_pixel), MP_ROM_PTR(&Kandinsky_get_pixel_obj)}, {MP_ROM_QSTR(MP_QSTR_draw_string), MP_ROM_PTR(&Kandinsky_draw_string_obj)}, + {MP_ROM_QSTR(MP_QSTR_CGEXT_Enable_Wide_Screen), MP_ROM_PTR(&Kandinsky_CGEXT_Enable_Wide_Screen_obj)}, + {MP_ROM_QSTR(MP_QSTR_CGEXT_Disable_Wide_Screen), MP_ROM_PTR(&Kandinsky_CGEXT_Disable_Wide_Screen_obj)}, + {MP_ROM_QSTR(MP_QSTR_CGEXT_Is_Wide_Screen_Enabled), MP_ROM_PTR(&Kandinsky_CGEXT_Is_Wide_Screen_Enabled_obj)}, }; STATIC MP_DEFINE_CONST_DICT(kandinsky_module_globals, kandinsky_module_globals_table); From 226f82ec8c66a8264db6b6f2b5cf0e1ebd733d19 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Sun, 4 Feb 2024 17:29:36 +0100 Subject: [PATCH 15/27] Add Kandinsky Extension for fxCG to be able to use wide screen - bugfix NW sreen is 222px high usable, not 240px --- docs/sh/NWcompat_modules.md | 2 +- ports/sh/numworks/modkandinsky.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/sh/NWcompat_modules.md b/docs/sh/NWcompat_modules.md index c3151d7b9..2e272305d 100644 --- a/docs/sh/NWcompat_modules.md +++ b/docs/sh/NWcompat_modules.md @@ -43,7 +43,7 @@ Les fonctions suivantes sont des ajouts pour tirer partie de l'écran large de l Note 1 : après avoir réalisé un tracé dans la zone étendue, il faut que celle-ci soit active pour permettre son effacement (typiquement via un appel à la fonction `fill_rect()` avec les paramètres adéquats). -Note 2 : En mode non étendu (par défaut à l'initialisation du module `Kandinsky`) les coordonnées de l'écran vont de (0,0) à (319,239) (avec une partie visible allant de (0,0) à (319,223) du fait de la hauteur réduite de l'écran de la fxCG). En mode étendu, les coordonnées de l'écran vont de (-38,0) à (358,239) (avec une partie visible allant de (-38,0) à (358,223) du fait de la hauteur réduite de l'écran de la fxCG) +Note 2 : En mode non étendu (par défaut à l'initialisation du module `Kandinsky`) les coordonnées de l'écran vont de (0,0) à (319,221) centré sur l'écran de la fxCG. En mode étendu, les coordonnées de l'écran vont de (-38,-1) à (358,223). ## `ion` diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index 7cada6072..453933796 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -22,7 +22,7 @@ extern unsigned int timer_altered[9]; #define DELTAXNW \ ((DWIDTH - 320) / 2) // we center the NW screen on Casio's screen -#define DELTAYNW 0 // NW screen will be cut in the bottom +#define DELTAYNW 1 // NW screen will be cut in the bottom /* Definition of color on Numworks */ @@ -79,7 +79,7 @@ static mp_obj_t Kandinsky_init(void) { nw.left = DELTAXNW; nw.top = DELTAYNW; nw.right = 320 + DELTAXNW; - nw.bottom = 240 + DELTAYNW; + nw.bottom = 222 + DELTAYNW; dwindow_set(nw); is_dwindowed = true; // we mark as windowed @@ -252,7 +252,7 @@ static mp_obj_t Kandinsky_CGEXT_Disable_Wide_Screen( void ) { nw.left = DELTAXNW; nw.top = DELTAYNW; nw.right = 320 + DELTAXNW; - nw.bottom = 240 + DELTAYNW; + nw.bottom = 222 + DELTAYNW; dwindow_set(nw); is_dwindowed = true; // we mark as windowed From 9156a21afa1a5f577c04172b00eec69fdb4f9c87 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Sun, 4 Feb 2024 19:38:12 +0100 Subject: [PATCH 16/27] Added missing safegard #ifdef FXCG50 for correct build of fx9860G version without NW modules --- ports/sh/numworks/ion.c | 4 ++++ ports/sh/numworks/modkandinsky.c | 4 ++++ ports/sh/numworks/time.c | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/ports/sh/numworks/ion.c b/ports/sh/numworks/ion.c index b86315d19..ea7e4cc30 100644 --- a/ports/sh/numworks/ion.c +++ b/ports/sh/numworks/ion.c @@ -1,3 +1,5 @@ +#ifdef FXCG50 + //---------------------------------------------------------------------------// // ____ PythonExtra // //.-'`_ o `;__, A community port of MicroPython for CASIO calculators. // @@ -172,3 +174,5 @@ const mp_obj_module_t ion_module = { }; MP_REGISTER_MODULE(MP_QSTR_ion, ion_module); + +#endif // FXCG50 \ No newline at end of file diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index 453933796..9407f5ab0 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -1,3 +1,5 @@ +#ifdef FXCG50 + //---------------------------------------------------------------------------// // ____ PythonExtra // //.-'`_ o `;__, A community port of MicroPython for CASIO calculators. // @@ -299,3 +301,5 @@ const mp_obj_module_t kandinsky_module = { }; MP_REGISTER_MODULE(MP_QSTR_kandinsky, kandinsky_module); + +#endif //FXCG50 \ No newline at end of file diff --git a/ports/sh/numworks/time.c b/ports/sh/numworks/time.c index debcdd0ec..157c1eaee 100644 --- a/ports/sh/numworks/time.c +++ b/ports/sh/numworks/time.c @@ -1,3 +1,5 @@ +#ifdef FXCG50 + //---------------------------------------------------------------------------// // ____ PythonExtra // //.-'`_ o `;__, A community port of MicroPython for CASIO calculators. // @@ -109,3 +111,5 @@ const mp_obj_module_t time_module = { }; MP_REGISTER_MODULE(MP_QSTR_time, time_module); + +#endif //FXCG50 \ No newline at end of file From 9bc05bbf035e186ec837db5919f421e1244914d8 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Mon, 5 Feb 2024 09:20:35 +0100 Subject: [PATCH 17/27] Improved NW modules documentation --- ...compat_modules.md => NWcompat_modules_fr.md} | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) rename docs/sh/{NWcompat_modules.md => NWcompat_modules_fr.md} (76%) diff --git a/docs/sh/NWcompat_modules.md b/docs/sh/NWcompat_modules_fr.md similarity index 76% rename from docs/sh/NWcompat_modules.md rename to docs/sh/NWcompat_modules_fr.md index 2e272305d..39571f1ab 100644 --- a/docs/sh/NWcompat_modules.md +++ b/docs/sh/NWcompat_modules_fr.md @@ -1,8 +1,11 @@ -# utilisation des modules `kandinsky`, `ion` et `time` de l'implémentation Python sur `NumWorks` +# Utilisation des modules `kandinsky`, `ion` et `time` de l'implémentation Python `Numworks` -PythonExtra offre la possibilité d'utiliser certains modules de la Numworks afin de rendre les scripts de cette machine compatible en l'état sur la fxCG50 (pas de support sur la fx9860G pour cause de mémoire insuffisante). +PythonExtra offre la possibilité d'utiliser certains modules de la Numworks afin de rendre les scripts de cette machine compatible en l'état sur Casio fx-CG50 (pas de support sur la fx9860G pour cause de mémoire insuffisante et d'abence d'écran couleur). + +Il s'agit d'un Work in Progress (WIP) et le support est sujet à tests approfondis à ce stade. Le port concerne les modules `kandinsky`, `ion` et `time` de la Numworks qui sont spécifiques à la machine et sont désormais supportés via cette implémentation. Les modules `math`, `cmath`, `random` étant identiques entre la version `Numworks` et les modules `builtins` de MicroPython, ils ne font donc pas partie de cette implémentation mais sont parfaitement utilisables sans modification dans les scripts. + +Note : les modules `turtle` et `matplotlib.pyplot` ne sont pas repris dans cette implémentation. Il est possible d'utiliser les modules `turtle`, `matplotlib` et `casioplot` de Casio Education qui sont parfaitement fonctionnels et fournis en example dans `ports/sh/examples`. -Il s'agit d'un Work in Progress (WIP) et le support est à ce stade partiel, néanmoins les modules `kandinsky`, `ion` et `time` de la NumWorks sont supportés. ## `kandinsky` @@ -50,9 +53,9 @@ Note 2 : En mode non étendu (par défaut à l'initialisation du module `Kandins Le module `ion` donne accès à la fonction `keydown(k)` qui renvoie True si la touche k placée en argument est appuyée et False sinon. -La "conversion" des touches entre la machine NumWorks et Casio fxCG50 se fait selon le mapping suivant : +La "conversion" des touches entre la machine Numworks et Casio fxCG50 se fait selon le mapping suivant : -| NumWorks | Casio fxCG50 | +| Numworks | Casio fxCG50 | |----------|--------------| | KEY_LEFT | KEY_LEFT | | KEY_UP | KEY_UP | @@ -101,6 +104,8 @@ La "conversion" des touches entre la machine NumWorks et Casio fxCG50 se fait se | KEY_ANS | KEY_NEG | | KEY_EXE | KEY_EXE | +Note : la fonction `keydown(k)` peut théoriquement être appelée sur `Numworks` avec le numéro de la touche correspondante (par exemple 12 pour la touche `KEY_ONOFF`), mais ceci n'est pas supporté par PythonExtra. Il convient donc de nommer explicitement les touches via leur code `KEY_xxx`. + ## `time` @@ -109,5 +114,3 @@ Le module `time` donne accès à deux fonctions : - `monotonic()` : Renvoie la valeur de lʼhorloge au moment où la fonction est appelée. - `sleep(t)` : Suspend lʼexécution pendant t secondes. - - From d4dcc59d7147f00103e0a5576767ae9bb9820f93 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Mon, 5 Feb 2024 16:37:26 +0100 Subject: [PATCH 18/27] Translated NW modules documentation in English --- docs/sh/NWcompat_modules_en.md | 116 +++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 docs/sh/NWcompat_modules_en.md diff --git a/docs/sh/NWcompat_modules_en.md b/docs/sh/NWcompat_modules_en.md new file mode 100644 index 000000000..938531f1d --- /dev/null +++ b/docs/sh/NWcompat_modules_en.md @@ -0,0 +1,116 @@ +# Using Numworks' modules `kandinsky`, `ion` and `time` with PythonExtra + +PythonExtra offers the possibility of using certain Numworks modules in order to make the scripts of this machine compatible as is on Casio fx-CG50 (no support on the fx9860G due to insufficient memory and lack of color screen ). + +This is a Work in Progress (WIP) and the support is subject to extensive testing at this stage. The port concerns the Numworks `kandinsky`, `ion` and `time` modules which are machine specific and are now supported via this implementation. The `math`, `cmath`, `random` modules being identical between the `Numworks` version and the `builtins` modules of MicroPython, they are therefore not part of this implementation but are perfectly usable without modification in the scripts. + +Note: the `turtle` and `matplotlib.pyplot` modules are not included in this implementation. It is possible to use the `turtle`, `matplotlib` and `casioplot` modules from Casio Education which are perfectly functional and provided as an example in `ports/sh/examples`. + + +## `kandinsky` + +The `kandinsky` module provides support for graphics primitives via high-performance `gint` routines. All the functions of this module are available: + +- `color(r,g,b)`: Generates the value of the color r,g,b. You can also simply use a tuple to define a color: (r,g,b). + +- `get_pixel(x,y)`: Returns the color of the pixel at x,y coordinates as a tuple (r,g,b). + +- `set_pixel(x,y,color)`: Lights the pixel x,y of the color color. + +- `draw_string(text,x,y,[color1],[color2])`: Displays the text at x,y coordinates. The arguments color1 (text color) and color2 (text background color) are optional. + +- `fill_rect(x,y,width,height,color)`: Fills a rectangle of width width and height height with the color color at the point of x and y coordinates. + +The module also offers a certain number of colors explicitly named and accessible by a character string. The following values ​​can be used instead of the color parameters of the `kandinsky` functions: +- "red", "r" +- "green", "g" +- "blue", "b" +- "black", "k" +- "white", "w" +- "yellow", "y" +- "pink" +- "magenta" +- "grey", "gray" +- "purple" +- "orange" +- "cyan" +- "brown" + +The following functions are additions to take advantage of the wide screen of the fxCG and are therefore an extension of the `Kandinsky` module. They are therefore by definition not compatible with Python Numwork. These functions can be recognized by their names which all begin with `CGEXT_`: + +- `CGEXT_Enable_Wide_Screen()`: Enables the fxCG wide screen, no settings are necessary. The x-coordinates of the physical screen can be negative to encroach on the left white stripe and greater than 319 pixels to encroach on the right white stripe; + +- `CGEXT_Disable_Wide_Screen()`: Cancels the activation of the fxCG extended screen, no settings are necessary. The x coordinates of the physical screen will be constrained between 0 and 320 pixels. Beyond that, the route will not be carried out. + +- `CGEXT_Is_Wide_Screen_Enabled()`: Returns `True` if the extended screen is active and `False` otherwise. + +Note 1: after having made a plot in the extended area, it must be active to allow its deletion (typically via a call to the `fill_rect()` function with the appropriate parameters). + +Note 2: In non-extended mode (by default when initializing the `Kandinsky` module) the screen coordinates go from (0,0) to (319,221) centered on the fxCG screen. In extended mode, the screen coordinates range from (-38,-1) to (358,223). + + +## `ion` + +The `ion` module gives access to the `keydown(k)` function which returns True if the key k placed as an argument is pressed and False otherwise. + +The “conversion” of the keys between the Numworks machine and Casio fxCG50 is done according to the following mapping: + +| Numworks | Casio fxCG50 | +|----------|--------------| +| KEY_LEFT | KEY_LEFT | +| KEY_UP | KEY_UP | +| KEY_DOWN | KEY_DOWN | +| KEY_RIGHT | KEY_RIGHT | +| KEY_OK | KEY_F1 | +| KEY_BACK | KEY_EXIT | +| KEY_HOME | KEY_MENU | +| KEY_ONOFF | KEY_ACON | +| KEY_SHIFT | KEY_SHIFT | +| KEY_ALPHA | KEY_ALPHA | +| KEY_XNT | KEY_XOT | +| KEY_VAR | KEY_VARS | +| KEY_TOOLBOX | KEY_OPTN | +| KEY_BACKSPACE | KEY_DEL | +| KEY_EXP | KEY_EXP | +| KEY_LN | KEY_LN | +| KEY_LOG | KEY_LOG | +| KEY_IMAGINARY | KEY_F2 | +| KEY_COMMA | KEY_COMMA | +| KEY_POWER | KEY_POWER | +| KEY_SINE | KEY_SIN | +| KEY_COSINE | KEY_COS | +| KEY_TANGENT | KEY_TAN | +| KEY_PI | KEY_F3 | +| KEY_SQRT | KEY_F4 | +| KEY_SQUARE | KEY_SQUARE | +| KEY_SEVEN | KEY_7 | +| KEY_EIGHT | KEY_8 | +| KEY_NINE | KEY_9 | +| KEY_LEFTPARENTHESIS | KEY_LEFTP | +| KEY_RIGHTPARENTHESIS | KEY_RIGHTP | +| KEY_FOUR | KEY_4 | +| KEY_FIVE | KEY_5 | +| KEY_SIX | KEY_6 | +| KEY_MULTIPLICATION | KEY_MUL | +| KEY_DIVISION | KEY_DIV | +| KEY_ONE | KEY_1 | +| KEY_TWO | KEY_2 | +| KEY_THREE | KEY_3 | +| KEY_PLUS | KEY_ADD | +| KEY_MINUS | KEY_SUB | +| KEY_ZERO | KEY_0 | +| KEY_DOT | KEY_DOT | +| KEY_EE | KEY_F5 | +| KEY_ANS | KEY_NEG | +| KEY_EXE | KEY_EXE | + +Note: the `keydown(k)` function can theoretically be called on `Numworks` with the corresponding key number (for example 12 for the `KEY_ONOFF` key), but this is not supported by PythonExtra. It is therefore appropriate to explicitly name the keys via their code `KEY_xxx`. + + +## `time` + +The `time` module gives access to two functions: + +- `monotonic()`: Returns the clock value at the time the function is called. + +- `sleep(t)`: Suspends execution for t seconds. From 2cb2bc6e67d1994a45478f76773981c6aa410613 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Wed, 7 Feb 2024 21:30:59 +0100 Subject: [PATCH 19/27] add CGEXT_Set_Margin_Color() function in Kandinsky module --- docs/sh/NWcompat_modules_en.md | 2 ++ docs/sh/NWcompat_modules_fr.md | 2 ++ ports/sh/numworks/modkandinsky.c | 32 ++++++++++++++++++++++++-------- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/docs/sh/NWcompat_modules_en.md b/docs/sh/NWcompat_modules_en.md index 938531f1d..ddae72f6c 100644 --- a/docs/sh/NWcompat_modules_en.md +++ b/docs/sh/NWcompat_modules_en.md @@ -44,6 +44,8 @@ The following functions are additions to take advantage of the wide screen of th - `CGEXT_Is_Wide_Screen_Enabled()`: Returns `True` if the extended screen is active and `False` otherwise. +- `CGEXT_Set_Margin_Color( color )` : Paints the margin of the `Numworks` screen on fxCG with the specified color. + Note 1: after having made a plot in the extended area, it must be active to allow its deletion (typically via a call to the `fill_rect()` function with the appropriate parameters). Note 2: In non-extended mode (by default when initializing the `Kandinsky` module) the screen coordinates go from (0,0) to (319,221) centered on the fxCG screen. In extended mode, the screen coordinates range from (-38,-1) to (358,223). diff --git a/docs/sh/NWcompat_modules_fr.md b/docs/sh/NWcompat_modules_fr.md index 39571f1ab..5e767f06c 100644 --- a/docs/sh/NWcompat_modules_fr.md +++ b/docs/sh/NWcompat_modules_fr.md @@ -44,6 +44,8 @@ Les fonctions suivantes sont des ajouts pour tirer partie de l'écran large de l - `CGEXT_Is_Wide_Screen_Enabled()` : Retourne `True` si l'écran étendu est actif et `False` dans le cas contraire. +- `CGEXT_Set_Margin_Color( color )` : Trace les marge de la fxCG50 (pourtours de l'écran `Numworks`) de la couleur passée en argument. + Note 1 : après avoir réalisé un tracé dans la zone étendue, il faut que celle-ci soit active pour permettre son effacement (typiquement via un appel à la fonction `fill_rect()` avec les paramètres adéquats). Note 2 : En mode non étendu (par défaut à l'initialisation du module `Kandinsky`) les coordonnées de l'écran vont de (0,0) à (319,221) centré sur l'écran de la fxCG. En mode étendu, les coordonnées de l'écran vont de (-38,-1) à (358,223). diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index 9407f5ab0..1c0e5d02f 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -22,10 +22,14 @@ extern bool is_dwindowed; extern bool is_timered; extern unsigned int timer_altered[9]; +/* Parameters used in windowed mode to center the screen of the NW in the fxCG screen*/ #define DELTAXNW \ ((DWIDTH - 320) / 2) // we center the NW screen on Casio's screen #define DELTAYNW 1 // NW screen will be cut in the bottom +/* refresh rate of the screen */ +#define TARGET_FPS 30 + /* Definition of color on Numworks */ // Data can be found here @@ -85,7 +89,7 @@ static mp_obj_t Kandinsky_init(void) { dwindow_set(nw); is_dwindowed = true; // we mark as windowed - int t = timer_configure(TIMER_TMU, 33333, GINT_CALL(callback)); + int t = timer_configure(TIMER_TMU, (1000000/TARGET_FPS), GINT_CALL(callback)); if (t >= 0) { timer_start(t); is_timered = true; // there is a timer altered from this module @@ -266,18 +270,29 @@ static mp_obj_t Kandinsky_CGEXT_Is_Wide_Screen_Enabled( void ) { return mp_obj_new_bool( is_dwindowed ); } +static mp_obj_t Kandinsky_CGEXT_Set_Margin_Color( mp_obj_t color ) { + + color_t colorside = NW_BLACK; + colorside = Internal_Treat_Color(color); + + Kandinsky_CGEXT_Enable_Wide_Screen(); + dclear(colorside); + Kandinsky_CGEXT_Disable_Wide_Screen(); + + return mp_obj_new_bool( is_dwindowed ); +} + +/* Extension of Kandinsky for fxCG - all names starting with "CGEXT_" */ MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_CGEXT_Enable_Wide_Screen_obj, Kandinsky_CGEXT_Enable_Wide_Screen); MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_CGEXT_Disable_Wide_Screen_obj, Kandinsky_CGEXT_Disable_Wide_Screen); MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_CGEXT_Is_Wide_Screen_Enabled_obj, Kandinsky_CGEXT_Is_Wide_Screen_Enabled); - +MP_DEFINE_CONST_FUN_OBJ_1(Kandinsky_CGEXT_Set_Margin_Color_obj, Kandinsky_CGEXT_Set_Margin_Color); +/* Standard Kandinsky function as per Numworks specification */ MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_init_obj, Kandinsky_init); -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_set_pixel_obj, 3, 3, - Kandinsky_set_pixel); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_set_pixel_obj, 3, 3, Kandinsky_set_pixel); MP_DEFINE_CONST_FUN_OBJ_2(Kandinsky_get_pixel_obj, Kandinsky_get_pixel); -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_draw_string_obj, 3, 5, - Kandinsky_draw_string); -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_fill_rect_obj, 5, 5, - Kandinsky_fill_rect); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_draw_string_obj, 3, 5, Kandinsky_draw_string); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_fill_rect_obj, 5, 5, Kandinsky_fill_rect); MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_color_obj, 3, 3, Kandinsky_color); STATIC const mp_rom_map_elem_t kandinsky_module_globals_table[] = { @@ -291,6 +306,7 @@ STATIC const mp_rom_map_elem_t kandinsky_module_globals_table[] = { {MP_ROM_QSTR(MP_QSTR_CGEXT_Enable_Wide_Screen), MP_ROM_PTR(&Kandinsky_CGEXT_Enable_Wide_Screen_obj)}, {MP_ROM_QSTR(MP_QSTR_CGEXT_Disable_Wide_Screen), MP_ROM_PTR(&Kandinsky_CGEXT_Disable_Wide_Screen_obj)}, {MP_ROM_QSTR(MP_QSTR_CGEXT_Is_Wide_Screen_Enabled), MP_ROM_PTR(&Kandinsky_CGEXT_Is_Wide_Screen_Enabled_obj)}, + {MP_ROM_QSTR(MP_QSTR_CGEXT_Set_Margin_Color), MP_ROM_PTR(&Kandinsky_CGEXT_Set_Margin_Color_obj)}, }; STATIC MP_DEFINE_CONST_DICT(kandinsky_module_globals, kandinsky_module_globals_table); From 7fdf3aeb2e5f100eb5576ea49ced307a9e6f6d21 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Thu, 8 Feb 2024 21:01:31 +0100 Subject: [PATCH 20/27] added automatic refresh and video output support on Kandinsky module --- ports/sh/debug.c | 5 +++++ ports/sh/debug.h | 4 ++++ ports/sh/main.c | 13 ++++++++++++- ports/sh/mpconfigport.h | 5 ++++- ports/sh/numworks/modkandinsky.c | 5 +++-- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/ports/sh/debug.c b/ports/sh/debug.c index 3c2b436b0..1cec7f702 100644 --- a/ports/sh/debug.c +++ b/ports/sh/debug.c @@ -125,4 +125,9 @@ void pe_debug_run_videocapture(void) } } +void pe_debug_close(void) +{ + usb_close(); +} + #endif /* PE_DEBUG */ diff --git a/ports/sh/debug.h b/ports/sh/debug.h index 293169817..f531c9835 100644 --- a/ports/sh/debug.h +++ b/ports/sh/debug.h @@ -39,6 +39,9 @@ void pe_debug_toggle_videocapture(void); /* Send a video capture frame if video capture is enabled. */ void pe_debug_run_videocapture(void); +/* Close the debugging ressources */ +void pe_debug_close(void); + #if !PE_DEBUG #define PE_DEBUG_NOOP do {} while(0) #define pe_debug_init(...) PE_DEBUG_NOOP @@ -47,6 +50,7 @@ void pe_debug_run_videocapture(void); #define pe_debug_screenshot(...) PE_DEBUG_NOOP #define pe_debug_toggle_videocapture(...) PE_DEBUG_NOOP #define pe_debug_run_videocapture(...) PE_DEBUG_NOOP +#define pe_debug_close(...) PE_DEBUG_NOOP #endif #endif /* __PYTHONEXTRA_DEBUG_H */ diff --git a/ports/sh/main.c b/ports/sh/main.c index 6abb155d4..be17e8218 100644 --- a/ports/sh/main.c +++ b/ports/sh/main.c @@ -64,7 +64,7 @@ struct pe_globals PE = { 0 }; bool is_dwindowed = false; bool is_timered = false; unsigned int timer_altered[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; - +bool is_refreshed_required = false; //=== Hook for redirecting stdout/stderr to the shell ===// @@ -179,6 +179,15 @@ void pe_enter_graphics_mode(void) PE.shell->widget.update = 0; } +void pe_refresh_graphics(void) +{ + /* refresh graphical output on request by setting + is_refresh_graphics to true */ + dupdate(); + pe_debug_run_videocapture(); + is_refreshed_required = false; +} + void pe_draw(void) { dclear(C_WHITE); @@ -539,6 +548,8 @@ int main(int argc, char **argv) //=== Deinitialization ===// + pe_debug_close(); + gc_sweep_all(); mp_deinit(); console_destroy(PE.console); diff --git a/ports/sh/mpconfigport.h b/ports/sh/mpconfigport.h index 31da969b3..7d0880617 100644 --- a/ports/sh/mpconfigport.h +++ b/ports/sh/mpconfigport.h @@ -103,8 +103,11 @@ void pe_after_python_exec( /* Command executed regularly during execution */ extern void pe_draw(void); extern widget_shell *pe_shell; +extern void pe_refresh_graphics(void); +extern bool is_refreshed_required; #define MICROPY_VM_HOOK_LOOP \ - { if(pe_shell->widget.update) pe_draw(); } + { if(pe_shell->widget.update) pe_draw(); \ + if(is_refreshed_required) pe_refresh_graphics(); } /* extra built in names to add to the global namespace #define MICROPY_PORT_BUILTINS \ diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index 1c0e5d02f..79a71c913 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -21,6 +21,7 @@ extern font_t numworks; extern bool is_dwindowed; extern bool is_timered; extern unsigned int timer_altered[9]; +extern bool is_refreshed_required; /* Parameters used in windowed mode to center the screen of the NW in the fxCG screen*/ #define DELTAXNW \ @@ -28,7 +29,7 @@ extern unsigned int timer_altered[9]; #define DELTAYNW 1 // NW screen will be cut in the bottom /* refresh rate of the screen */ -#define TARGET_FPS 30 +#define TARGET_FPS 20 /* Definition of color on Numworks */ @@ -57,7 +58,7 @@ extern unsigned int timer_altered[9]; // There are possibly some others to be listed correctly static int callback(void) { - dupdate(); + is_refreshed_required = true; return TIMER_CONTINUE; } From 0f386bd779dc18857ab76ef96547884cd95b6077 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Sat, 10 Feb 2024 10:33:44 +0100 Subject: [PATCH 21/27] made fully compatible Ion::keydown() function with key number support + update of documentation accordingly --- docs/sh/NWcompat_modules_en.md | 102 ++++++++++++------------ docs/sh/NWcompat_modules_fr.md | 102 ++++++++++++------------ ports/fxcg50/.gitignore | 2 +- ports/fxcg50/Makefile | 2 +- ports/fxcg50/icon-selNW.png | Bin 0 -> 15115 bytes ports/fxcg50/icon-selNWUSB.png | Bin 0 -> 16449 bytes ports/fxcg50/icon-unsNW.png | Bin 0 -> 11355 bytes ports/fxcg50/icon-unsNWUSB.png | Bin 0 -> 12678 bytes ports/sh/.gitignore | 1 + ports/sh/numworks/ion.c | 140 ++++++++++++++++++++------------- 10 files changed, 192 insertions(+), 157 deletions(-) create mode 100644 ports/fxcg50/icon-selNW.png create mode 100644 ports/fxcg50/icon-selNWUSB.png create mode 100644 ports/fxcg50/icon-unsNW.png create mode 100644 ports/fxcg50/icon-unsNWUSB.png diff --git a/docs/sh/NWcompat_modules_en.md b/docs/sh/NWcompat_modules_en.md index ddae72f6c..ba492d73f 100644 --- a/docs/sh/NWcompat_modules_en.md +++ b/docs/sh/NWcompat_modules_en.md @@ -57,56 +57,58 @@ The `ion` module gives access to the `keydown(k)` function which returns True if The “conversion” of the keys between the Numworks machine and Casio fxCG50 is done according to the following mapping: -| Numworks | Casio fxCG50 | -|----------|--------------| -| KEY_LEFT | KEY_LEFT | -| KEY_UP | KEY_UP | -| KEY_DOWN | KEY_DOWN | -| KEY_RIGHT | KEY_RIGHT | -| KEY_OK | KEY_F1 | -| KEY_BACK | KEY_EXIT | -| KEY_HOME | KEY_MENU | -| KEY_ONOFF | KEY_ACON | -| KEY_SHIFT | KEY_SHIFT | -| KEY_ALPHA | KEY_ALPHA | -| KEY_XNT | KEY_XOT | -| KEY_VAR | KEY_VARS | -| KEY_TOOLBOX | KEY_OPTN | -| KEY_BACKSPACE | KEY_DEL | -| KEY_EXP | KEY_EXP | -| KEY_LN | KEY_LN | -| KEY_LOG | KEY_LOG | -| KEY_IMAGINARY | KEY_F2 | -| KEY_COMMA | KEY_COMMA | -| KEY_POWER | KEY_POWER | -| KEY_SINE | KEY_SIN | -| KEY_COSINE | KEY_COS | -| KEY_TANGENT | KEY_TAN | -| KEY_PI | KEY_F3 | -| KEY_SQRT | KEY_F4 | -| KEY_SQUARE | KEY_SQUARE | -| KEY_SEVEN | KEY_7 | -| KEY_EIGHT | KEY_8 | -| KEY_NINE | KEY_9 | -| KEY_LEFTPARENTHESIS | KEY_LEFTP | -| KEY_RIGHTPARENTHESIS | KEY_RIGHTP | -| KEY_FOUR | KEY_4 | -| KEY_FIVE | KEY_5 | -| KEY_SIX | KEY_6 | -| KEY_MULTIPLICATION | KEY_MUL | -| KEY_DIVISION | KEY_DIV | -| KEY_ONE | KEY_1 | -| KEY_TWO | KEY_2 | -| KEY_THREE | KEY_3 | -| KEY_PLUS | KEY_ADD | -| KEY_MINUS | KEY_SUB | -| KEY_ZERO | KEY_0 | -| KEY_DOT | KEY_DOT | -| KEY_EE | KEY_F5 | -| KEY_ANS | KEY_NEG | -| KEY_EXE | KEY_EXE | - -Note: the `keydown(k)` function can theoretically be called on `Numworks` with the corresponding key number (for example 12 for the `KEY_ONOFF` key), but this is not supported by PythonExtra. It is therefore appropriate to explicitly name the keys via their code `KEY_xxx`. +| Numworks | Casio fxCG50 | Numworks Key # | +|----------|--------------|---------------------| +| KEY_LEFT | KEY_LEFT | 0 | +| KEY_UP | KEY_UP | 1 | +| KEY_DOWN | KEY_DOWN | 2 | +| KEY_RIGHT | KEY_RIGHT | 2 | +| KEY_OK | KEY_F1 | 4 | +| KEY_BACK | KEY_EXIT | 5 | +| KEY_HOME | KEY_MENU | 6 | +| KEY_ONOFF | KEY_ACON | 7 | +| ... | ... | ... | +| KEY_SHIFT | KEY_SHIFT | 12 | +| KEY_ALPHA | KEY_ALPHA | 13 | +| KEY_XNT | KEY_XOT | 14 | +| KEY_VAR | KEY_VARS | 15 | +| KEY_TOOLBOX | KEY_OPTN | 16 | +| KEY_BACKSPACE | KEY_DEL | 17 | +| KEY_EXP | KEY_EXP | 17 | +| KEY_LN | KEY_LN | 19 | +| KEY_LOG | KEY_LOG | 20 | +| KEY_IMAGINARY | KEY_F2 | 21 | +| KEY_COMMA | KEY_COMMA | 22 | +| KEY_POWER | KEY_POWER | 23 | +| KEY_SINE | KEY_SIN | 24 | +| KEY_COSINE | KEY_COS | 25 | +| KEY_TANGENT | KEY_TAN | 26 | +| KEY_PI | KEY_F3 | 27 | +| KEY_SQRT | KEY_F4 | 28 | +| KEY_SQUARE | KEY_SQUARE | 29 | +| KEY_SEVEN | KEY_7 | 30 | +| KEY_EIGHT | KEY_8 | 31 | +| KEY_NINE | KEY_9 | 32 | +| KEY_LEFTPARENTHESIS | KEY_LEFTP | 33 | +| KEY_RIGHTPARENTHESIS | KEY_RIGHTP | 34 | +| ... | ... | ... | +| KEY_FOUR | KEY_4 | 36 | +| KEY_FIVE | KEY_5 | 37 | +| KEY_SIX | KEY_6 | 38 | +| KEY_MULTIPLICATION | KEY_MUL | 39 | +| KEY_DIVISION | KEY_DIV | 40 | +| ... | ... | ... | +| KEY_ONE | KEY_1 | 42 | +| KEY_TWO | KEY_2 | 43 | +| KEY_THREE | KEY_3 | 44 | +| KEY_PLUS | KEY_ADD | 45 | +| KEY_MINUS | KEY_SUB | 46 | +| ... | ... | ... | +| KEY_ZERO | KEY_0 | 48 | +| KEY_DOT | KEY_DOT | 49 | +| KEY_EE | KEY_F5 | 50 | +| KEY_ANS | KEY_NEG | 51 | +| KEY_EXE | KEY_EXE | 52 | ## `time` diff --git a/docs/sh/NWcompat_modules_fr.md b/docs/sh/NWcompat_modules_fr.md index 5e767f06c..6f2a70170 100644 --- a/docs/sh/NWcompat_modules_fr.md +++ b/docs/sh/NWcompat_modules_fr.md @@ -57,56 +57,58 @@ Le module `ion` donne accès à la fonction `keydown(k)` qui renvoie True si la La "conversion" des touches entre la machine Numworks et Casio fxCG50 se fait selon le mapping suivant : -| Numworks | Casio fxCG50 | -|----------|--------------| -| KEY_LEFT | KEY_LEFT | -| KEY_UP | KEY_UP | -| KEY_DOWN | KEY_DOWN | -| KEY_RIGHT | KEY_RIGHT | -| KEY_OK | KEY_F1 | -| KEY_BACK | KEY_EXIT | -| KEY_HOME | KEY_MENU | -| KEY_ONOFF | KEY_ACON | -| KEY_SHIFT | KEY_SHIFT | -| KEY_ALPHA | KEY_ALPHA | -| KEY_XNT | KEY_XOT | -| KEY_VAR | KEY_VARS | -| KEY_TOOLBOX | KEY_OPTN | -| KEY_BACKSPACE | KEY_DEL | -| KEY_EXP | KEY_EXP | -| KEY_LN | KEY_LN | -| KEY_LOG | KEY_LOG | -| KEY_IMAGINARY | KEY_F2 | -| KEY_COMMA | KEY_COMMA | -| KEY_POWER | KEY_POWER | -| KEY_SINE | KEY_SIN | -| KEY_COSINE | KEY_COS | -| KEY_TANGENT | KEY_TAN | -| KEY_PI | KEY_F3 | -| KEY_SQRT | KEY_F4 | -| KEY_SQUARE | KEY_SQUARE | -| KEY_SEVEN | KEY_7 | -| KEY_EIGHT | KEY_8 | -| KEY_NINE | KEY_9 | -| KEY_LEFTPARENTHESIS | KEY_LEFTP | -| KEY_RIGHTPARENTHESIS | KEY_RIGHTP | -| KEY_FOUR | KEY_4 | -| KEY_FIVE | KEY_5 | -| KEY_SIX | KEY_6 | -| KEY_MULTIPLICATION | KEY_MUL | -| KEY_DIVISION | KEY_DIV | -| KEY_ONE | KEY_1 | -| KEY_TWO | KEY_2 | -| KEY_THREE | KEY_3 | -| KEY_PLUS | KEY_ADD | -| KEY_MINUS | KEY_SUB | -| KEY_ZERO | KEY_0 | -| KEY_DOT | KEY_DOT | -| KEY_EE | KEY_F5 | -| KEY_ANS | KEY_NEG | -| KEY_EXE | KEY_EXE | - -Note : la fonction `keydown(k)` peut théoriquement être appelée sur `Numworks` avec le numéro de la touche correspondante (par exemple 12 pour la touche `KEY_ONOFF`), mais ceci n'est pas supporté par PythonExtra. Il convient donc de nommer explicitement les touches via leur code `KEY_xxx`. +| Numworks | Casio fxCG50 | Numworks Key # | +|----------|--------------|---------------------| +| KEY_LEFT | KEY_LEFT | 0 | +| KEY_UP | KEY_UP | 1 | +| KEY_DOWN | KEY_DOWN | 2 | +| KEY_RIGHT | KEY_RIGHT | 2 | +| KEY_OK | KEY_F1 | 4 | +| KEY_BACK | KEY_EXIT | 5 | +| KEY_HOME | KEY_MENU | 6 | +| KEY_ONOFF | KEY_ACON | 7 | +| ... | ... | ... | +| KEY_SHIFT | KEY_SHIFT | 12 | +| KEY_ALPHA | KEY_ALPHA | 13 | +| KEY_XNT | KEY_XOT | 14 | +| KEY_VAR | KEY_VARS | 15 | +| KEY_TOOLBOX | KEY_OPTN | 16 | +| KEY_BACKSPACE | KEY_DEL | 17 | +| KEY_EXP | KEY_EXP | 17 | +| KEY_LN | KEY_LN | 19 | +| KEY_LOG | KEY_LOG | 20 | +| KEY_IMAGINARY | KEY_F2 | 21 | +| KEY_COMMA | KEY_COMMA | 22 | +| KEY_POWER | KEY_POWER | 23 | +| KEY_SINE | KEY_SIN | 24 | +| KEY_COSINE | KEY_COS | 25 | +| KEY_TANGENT | KEY_TAN | 26 | +| KEY_PI | KEY_F3 | 27 | +| KEY_SQRT | KEY_F4 | 28 | +| KEY_SQUARE | KEY_SQUARE | 29 | +| KEY_SEVEN | KEY_7 | 30 | +| KEY_EIGHT | KEY_8 | 31 | +| KEY_NINE | KEY_9 | 32 | +| KEY_LEFTPARENTHESIS | KEY_LEFTP | 33 | +| KEY_RIGHTPARENTHESIS | KEY_RIGHTP | 34 | +| ... | ... | ... | +| KEY_FOUR | KEY_4 | 36 | +| KEY_FIVE | KEY_5 | 37 | +| KEY_SIX | KEY_6 | 38 | +| KEY_MULTIPLICATION | KEY_MUL | 39 | +| KEY_DIVISION | KEY_DIV | 40 | +| ... | ... | ... | +| KEY_ONE | KEY_1 | 42 | +| KEY_TWO | KEY_2 | 43 | +| KEY_THREE | KEY_3 | 44 | +| KEY_PLUS | KEY_ADD | 45 | +| KEY_MINUS | KEY_SUB | 46 | +| ... | ... | ... | +| KEY_ZERO | KEY_0 | 48 | +| KEY_DOT | KEY_DOT | 49 | +| KEY_EE | KEY_F5 | 50 | +| KEY_ANS | KEY_NEG | 51 | +| KEY_EXE | KEY_EXE | 52 | ## `time` diff --git a/ports/fxcg50/.gitignore b/ports/fxcg50/.gitignore index b6d29c07b..c5c5c4998 100644 --- a/ports/fxcg50/.gitignore +++ b/ports/fxcg50/.gitignore @@ -1,2 +1,2 @@ /icon.xcf -/PythonExtra.g3a +/*.g3a \ No newline at end of file diff --git a/ports/fxcg50/Makefile b/ports/fxcg50/Makefile index 8f3e96d49..69ae0d847 100644 --- a/ports/fxcg50/Makefile +++ b/ports/fxcg50/Makefile @@ -6,7 +6,7 @@ SH_LDFLAGS := -T fxcg50.ld -ljustui-cg -lm -lgint-cg -lc -lgint-cg -lgcc SH_ASSETS := img_modifier_states.png font_9.png font_13.png font_19.png PoliceNW.png SH_METADATA := fxconv-metadata.txt SH_CONVFLAGS := --cg - + all: PythonExtra.g3a PythonExtra.g3a: $(BUILD)/firmware.bin icon-uns.png icon-sel.png diff --git a/ports/fxcg50/icon-selNW.png b/ports/fxcg50/icon-selNW.png new file mode 100644 index 0000000000000000000000000000000000000000..fa0976934f431faba4b396dbac3d084b7cffb8ad GIT binary patch literal 15115 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 RWseC^kWrPclQawYzX04qr=I`- literal 0 HcmV?d00001 diff --git a/ports/fxcg50/icon-selNWUSB.png b/ports/fxcg50/icon-selNWUSB.png new file mode 100644 index 0000000000000000000000000000000000000000..49ebe14bf914a3fc83577e1dce9eb02e2e59c867 GIT binary patch literal 16449 zcmeIYWmH_vwl>;0L4pK#32u$MOK_K9fu_50r*R4H?(Xivosa-Qf?FUsA-HR>T=KsA z?6dd%zA^4N_mf|zN3ZU+s^)xZ&ZnMQwWK1{Ro`Huk)Qzp089mW8O`T^qvuTm75@2` zC?o6y0H9L!(tZom1i4c=Iy;zK*+Qvco{mr|sE3s~0N}Ayl4X?$t|G|!&5ec?kUQpR zNh^YYwYDZT(xG!zOZ#wW&6T3KNA_*Z8qj{x`?TZwJ4n0+>e9|r)vY^`x9k!Zgz{7T z{op-`kDtj}UPDhW`g^gH^BYe}Pv$dGU%#e1-^YoIjRhpJHRQMv2f~IE@=p&Jgpb}~ zmhX*sy^D%*WGN0$Lhf#)nEd>3dI*W~tbQ%kJc@;QimlyDC?8&|?I~HGEcLRV-%PaQ zbQ^nc4R~nE=hiX#qQqZ{ck-+v_#T~IT&~LwF2o-S9aTit<{Cw{_MXCSm zYPZgI>^5dy4jyYp|23!hFC zwY^kSzT9jbnrcJV)y%$|X7QdIGBfymNj0h_N8 z3X(bFD-yDDk%vdx)y1hOd3h9>+mkhwSaiLJ zdFPCjYMONdHZ3cl3yM`Wyf)pKN~tw(o6XHvf$(c@d^5^eb6QP1x;@I8C#2c*SS5 zrq#IfG1f%?zVu4`>4{JueXV*Rjj6l-afD=|<=Q2JN`PsE_(c>u3fF2fJem-*#j4b} zMuyh7PaI4(0;7S^LYNT@0p?+kVv4uBoF!>MkAJf`)NYHP6Z|&e-bwEG&E@htqxJph z5&>pS+6Og4=5nzq-_~ej_JB<;WZ>N=h@wzqHFuq1*}ziNRp!qwzA_YCwVuW%Pi}57 za#U8a(U`y%yYTmgsJU#% zr7Y$TB2)RuNtdLmtYRFdUj36!dHqMB+?Ep=gIcSrcGVsFBb5_GdqL+c(G}bUZ~K+S z_Qk^ay_B?4;TENgM83g4aH_B+7&s6s1*~0D9pkx*Qb%$8;S@i8w)&(lkLZr zp_sdGZY>5)UBXweL%Outwfm7>W1))veLvpw$aOiwpW=(BJB}QAPaU_AY|g}(OjKt} zXmy4vtFV_}#E~xS{0~heu$)bO!z7h>A@Q2!+vzT zvwT7~gJ0t{wZf-k*LU!~8FnvpQ_M;mtekKb1mpEE;OK8t*dn=Ba`*n@AE+ z(Tf#puU)5?n+I*?xk!e2NRH3hd2yuNlO+g`3AFJZs;8^66nYFDJF^36CnC70%AaHr z2KCs>nKX9kFg(Tb-_}XEROYq)@V;Kz!}s?}xD0AQ5uL$*?Gt^`}IQkYky(SF(b!Y<)sN8Gca`Z9QXHRk2Xd zU0O<4ibbkY&Q~_Cgx2Ch)=yVySc+eW&~h?W(aLMRjA9~T(kz9vkYQK{tkFT;4Xg`$ z9iJ-w_=(W&*xq|O-R5@di>GuGolKo6r$RPkr3RPXT&ytG3o}L=Ud&aR8`1h(QGM@3SA3g&m&6PrAKDoV|?Ru3{;@`53;`&aC0O1Irf+W;WMf3uZ_wsWU zR^C@brXb?$TZyBb4n^UrA6Ym7uge1KyQ~u%{iq$(JxGZkJZDZ$7TaZJk6@Mx@6@0C z;0P7p8<$g(IuZY7CBJj5lxXomB$T*&9QA+U2BQBAt0H>bQSJLlS#;knXbgK-r;@9b zE5?+P^{xR?wDtqQInuTohtG$=i5`YeCBf%KYYn}@Q~SWl(7euPje^Jc+pW&zm0ft* z6l<=pTbZ@n0ku2oG5sYn7t~JAsz_R|Sd|!I2lZu#s{&Zr9=)pQJ2vVsQK1T-Myj@7 zyjo^I(vu5k|Iwdrq7nj!}_NB0p9UaH1y?*Sc>_AnJ@O9H}x|P z(5usni}5^1GiSGuUqnBoK4z zAf_7DC?5>r3ISU&X3$c5{-!I*MtR--f~k9m8R?%V5zMV~f2Wj{TOq?)(_^ zZONSQI8jB@ zW2jY;sniT`+_DPeWeq3MV6EMFB}*-9;zP8oT1x)rIy$EM8JGYVY9J6`UR1rKU zMCR8lK{zjFM7_mO%`!M&F6d-lmFh=+eL5GrdE$uX%_tXM4DCp$T>S@+nN73cNoxJEH6Jm#gZo*_@VjlY#xG|5TboGmqm;({WHAncQ znJ`E96maO^2_<#KFl{c`0oo~q<|uTU>75q?>R#)b>5bMAePnueo*$QPK?;L2&{g3= zN#H``#5Z3^B=||z>cEKiDTeyuHB~J+USDIu?|EN4i94RNYs;3M*DY5M z&WBrC60y8Cj|pWZPlbo@3*h!SR*QwKYvANmSQ-ZezLAkw5f$6RJdFQclh4To%*_3a zKNX5(Q;CFQ4PY~aIJ_*%AyRo6BR+}4kD?nwAYH2a(L#)qsLK~`#x#e>LL%u?V|adW zTOPwm_rNWH+!7;J4@l=J*|A#~Tj#rM+b>ETN~PO&fVTt(@4p8~k2xf2-_jSd3N zlbNeWe~`Z6J&9At<``{0m-`CWVM+2Sj_*fo=CHr`XV2`T%Qcl9=lRLhtX;(ZM?9bkb{8+70J}qq5332IfUVp z!0sR`fs7Jl2`23<%)K_BK-h7_WI#fTjg3)62KmxjI5+q5?hb_14oY65rr0f!kqb$W zGTY-;n@#!^zEm-AmK*FsN2A4`f5;CB?mQI`X1zgbP$TMkSEB!N9QK9AI@Am69Sp^w z>+>THVwvfX$P>{((E3N0jl}KTXbP2dB60g*4jQZ?3O-8$EVO!Ra&-xc-g#;AI8d!R zP8n~wJ#rqi7%69_gYZuXtU_BSC!*vcwyRHmlZ6 zR6JP(-)d?z3ZPF4B4A&{=PqTD_JUMhKuMnMDRW(DnrvQCTGf!=Mtvq8b;JY0W{F#) zTm~KVw;6cEo5p$Kg;3P_d!L{}A2FXLOOz$UaAjZSM{E>gOLVK=5tPABYc=;8>qxG2lH;m}`2? z*;@llreO=bC=fe{OOztxVhU8q1ArO7$NXNxW>Lo5FYSxqOpcZ*rFS|%;1oer@U__^ zjr^IpLyU6%QfTw$Q9kcPs$QK6MJQtA$2-OdG3p|9r&|z57*)Pu69AKWO8b7K_ller zAs>lk+lcBZLhm!aCz|QxTG(_p6$w@W&<87hrImyVe1JHxIZh!b8qZc5aS+y&uj0dc z1W=6j*wu~3A`?8{;pF-XSK)5CA{WJrE*Mx4f4XzWWYNU4Us|-@B*xxjK?F`5k+$#> z$WALDLCGKV-9AifC!2sng)ZmMh@ zpZi4XrX0nOOC> zo4#-y9-_-g7m`B&p9(g%JCK#S6j-21Mk0`^Uk8PRe?7#1tgZtDZD&)Yj1m*NL4N#r z6-^65d_^akc1eG|1YzVLc!p_68|bk0oVNA6O(VCcO{atg{ly{*v(=F!Fs zvx{o0ZlpC4+q88Aju>57pf57=aZs0zQtdZ9BBsK{4=*J2rA}yQ#Jm=2qY@Y%LYQOT5xJra~FQ;yzNYo(?WGziS+X>Gk|d6p869GpcR zeHljitLV!^tfHNfSbU=5%fzy7J(05)0o2Hm_M%5*A3Om_*a}X4`1~qp;bcX#Z9ILY zV(Rd_-+Y(fS;}CDOwXd9@&JgMtMFJw^ z;~P_w;e)M$>7DRUNfR*b@+yRFjIToLx9I`66$a4`k;+qw$1uB0(_N0hw5S;zhSitn z)*&epzZ7!|lF&Z@KvFm(j((6=rWuiV#xS6QjKeG5W8;cgWa5BjgTS5pV@zqu=mViJ zBVd9pYFp#>Tvj|6CGKVptQuY!hhCk}ihNe+GU290iJ=CL0r_azv?#HQzj<>)KL!s4 z84z@jw|wqVzR{c~lT~nX<`=%M;cmR-vRbOsSs%^~?={5n>zvh;`cz%|A|N%r_60<& zmj!|8O?pBmiu%Gl4i^yvyf)Y*#5Db+2$CO!%h=LfLRX|-rzCrfOsR%)g=aRsN758b z#p1x^GCOxeDvFGaJ~-*#SS}qEJ+eHvoR5hwHj9^Oa4$`DP?3+EYHuwnQrlV_p+;(H z)GJaa1Go1Yt$C^G+k&B!g~g@SA>4RE6&B^7I+nENxk@n*Vs}a_m)}Bzq=_G}^{3+5Hm+Z7z~pQ>)OTzsq zPMG;w@5XbMZrmifC_@77!bcru0sV|q@Q9*#7m0f&1*GQ1psomF!zog+S!Yx=WqHVY zV<}DnQX(&~A(ipTMSuH7*raUhGe)oZ1U;js`>@bUy9qc-WZu5+o5r(B<4O$4&kb+; zmv+9-c`FN2Xmz~K(^p3*S@v&+OD!KS;imBC`ZW+_MULPaeX6m94u*%2j3hsO#7Op) zcC4g^FbRko1}axgD-h<^4hTl#C`fcb8BSZ8dav8N+Gz4F{bck%;=PRaqEf6Z7XQsmF@CNY==se*|#OvclxnakT(sef`pjG2w`x=(oE7ti6 z>_sz#XL`(yC-Bqxv2i0f4tU&MUnM^lu4aiy&ii3uHr7PRj63s962g5IU<3ZWkl80; zcsFvOgF#w$DGqv5(jD0{aPP@d{yhYlh6Mh2H<~Vn{1op;a7$O%FE#!sb@^X1?D?4! zh7apwzeNgcRoOnZro3=xX_6(fLSr71Ur6+GgfkElGDM{fjKbsXMUX2OSx)r-)n(T{ zgzXeGnpB7xNX#16yU;@J{L2khbDSC&k<~~LI>q7Xufc7zXes$3qw-U6Rg6x3C?fAI z=0a>`fCVy5k#MnA-YE9#L=h4+n)9>Pn6v7wEb%KXR z#rA^wZCick>9BjCgy>xH1~8dTjlnKvYdRd1_NjMp$=MERWOzv%F-s#O=(58^n-*Km zW0yFS0J^!nwQ=zh!1?8d^>^LYSVq`UB&#RopnZi^McC&2m^U7Cfilp>SNkJr{;zh{ z%v!2Pi4HS|H8a}CVCzy>N^Rv?1)g#WlgJ@j|GW~yV@Wr}`N&ZD*~o7s>g7Cg_?zDI#lXx}V)ifP-z@C&OoSW5=BMUv8IC4?ZE8X4uA`x8%^P06Qv+dQDu((%qp#r+1jV6qP1d%7 z@Zm4uBymrBq@IvEk-o|a=3M^3s3$Z`S~)XFe9AdrE`Mq?;&CN$v!UyLVbUy=LwTd4 z&FAzz9uaIL4P8*ft=kBCHR@kFqbV$FNrZynl0^RCP8gZ-(ncMg(I%HV zo0+y*5Ds&!fU`EuA4RM9h0+U{{cLGjpPoV20uzaJJwU)8XI$4d8jN5tIlErE9AmS@ ztNYz&yu$M`6wt6SPzQIhH@2aft@OP!+Siv|$vTUSdrD0OJ##R`U}iB(D$VNTx1Xf} z;n8n*LZle-adhK1-HVH5CUB`q^l0Ffw{r0iBjXR)y^uc}|qbAV`O9B$;YaCD# z%4j@%*_HeBZfWWTwL9z<#eJ{&mB098izx0AyKmK&oAT|0APB}GfOe*~`rFg{%dF*@ ztVM$I{fI+6(0qy`X}ntvQ1Wk*kCP51!k-d@{vHSh*tLWmFl^~xgxMq%T zgY>x0$UkGn@cqpU)!hB+I);2_#Fk$gv<-%Y7%qt;Ah($)_>3pbO}tRaC@8psw;?Js zpxUY?()BRyW4Eg@Fnu@e=Ir|=Y)jV?))g=~pvZ@q___8+sgASq;gPa!H{DCN9D4!f zk9o}mI+so_9T5V;D#xTCJRbAJ)6tj9szD2q^a8J(ZrqWkv_+dvy$u1=OK$UuUss?$ zf!}Hk({0(V-_tT3&l2XIufVhXS{{ zVz2Ww7fJ)%CKDFaw0C8Ae~GWS8O2K77<5Dsiku9+4Iw+&_&il;iE$!>b%GCf`dO9+ zu5s?S%OzMg$pis`BnSOxZyZ0F-NpIOr{Q=ilhvK&^yZq#z1h^4; zo$h>!9Kq{M{)ONM@T7iuh0AR)t<^o{TY5Fw9d39IdP;+0&x4{ElS-~js~n1Sq;suN zA5{!hV{N1?YITBWR^-=@2zXc+^hA211z&A2cEtkSUa zh2+ZKsD6RUq7c?oLjsE*cRraf8GuSNTrL8XM8Q^llssp%OtkgXo*8+rla2;IB$Hsn zdcqH1v4x#4(cEtJ^NtypaC_$n%Lay!-dK~f?yC1?6IK_}z`x2?`y6&={bF7!=yeXs zKs-064A)&RiYcp5PW;p++Hiiw>&#V@L5W*#m^qU*KE{%_QLwqjD1rz=r$Q#`!}oew zOD4>>HZ^DYrY=)u6oILJ;#mOgww6!p2L;aiAg&^J^*1|T<7uW%);-;H4(d?N$k7sH z&=o#jZRpfYXmJ~ilva!I(&9{bRK^l8roE$Fj$C#UJUZ5N6*8~Q5hGt4O~=B|bGvn# zRs5uG7w}O*g@C>RZdTCUrix1Ibrq;l+f?K1njL8pW$_bMyV z{w;Y@O{NWI4N|w=9Wh#S6!gmkB*#NuXePhN;Z;ebO}=5cP67~t%F!DE{35TZH6*&8 zF4DegE*Hpkgp8$deTke+H?MF*wL;5l_btfM(~1M4Sy_rC_1Zuu#EAbgAa9{aOP)3& z6a^)+-1hkaXVtJ_JPu-psG zc^6S@x4J5XVXaSp28FSFG_slc(Ln|0H(l}@KX|btw)c0HNy_CO%+e?mMD2q$#^2QF zB;bYaPlm@%rSpWRar6?USJo8m5?0pUPgBHf3N;<3&Z))J7wx7w;&2A;p5x*K0!RQ- z9g#2+3l|1%-~N!LVx^9^#hbsp-mb;ua~F&q4eZZ zhFEn`m~>im_+^dfE@O5qUfCkuj9ac<@5Ir% zG3;=>-{P%`uYAd*u4Q6Ne$fpM%7EsV_K!{N&C-4_GuGj!8~3jAdnVwF5NEywiF|hV zOd?{m)S_x?$l5-zC>J8sG+Q!n$kuV{QFhP1{p;F8d?Pfno0b=uSxg)7W{APD63r z8;o{Qjk5vj8}ISiyZ!IkrTv@~I%}* z|FN>*c|Ae4Z-S`&X9=IHYy3!c>RGfJp>(4=Bo&fGJi<49 z&Fkx5%;x$A^z~5mB(Wa=PHv8xums%S3wQ?o9*)a#?l%sw{K$Yi!S%T*fmu$tD;w*L+HT`2~32a?zP@@nS2fG|JR8K>Up%19bsATRFn49PFw7V1mpXTw$U#G|%-^{}G>^ql(Hu;q6`i z!NM~i93G(O=Njxl4m&%JfAw&I$+|s*{3D?MsfUa9^MV-;O{j~5t1}oX>jt%l(f%t0 z1pH5bM^|UtztVw#IiR*syJu6E=T(D zfKL#_#s>xQvhhHGW^5n`H{ib>KwdB} zl#7R-4I*Iv48h3>WD_ul^09G2c|c%J0ZyQ~fWW^%s5x6bs}f}UuTlMhf;>a7L3xb(HJBZRKTiLsM{AY!>l^s+I2KvJ$CqIy%_s zbN{E2F4WoOS&4rzIf3llf8+k?7NO^4o{0th(dje5Umnl72uV9bK`;krZ3hQiQJOy? zQT_4!XSK@naDsqfAQ=!0`V0!>;u7Kn3UP63KkxAJ2yyYV0J(&K|6=a|u`>7k|FZrw zc&J4F?s9o6m*@69|0?=>L}@{t{yzHq(AMg&p+rUX*Psvrf&Wgy1>^>W{1xXj*56fN zOOU+<^!e=Z54rwFxz+zf3W8vMAdk5j7aK2xhmVa1_}q170z6PQE&;yheg$zu`JsIO zj_%@M4s!=NLnSSqd3@&TS)PA!MaB46M=|}owYw$sPcJ+Z#`dhT|B*0`{|hNXxOvRL zUlY4a5ls3IMsy&CNOgmij+6^1qa#pf=ZY zegZ;Vz<*CsgyYYd|37pq!tp;S`)`APnJS)p|L?NrDd>4d-fX)E`FRK+~B(*)3 zx{XY#$#jT+=VUCdxE%L(cXqe7vf1s05HK}OO2MIP&`7DPlMG-V1ks;MydLa#S5f{k zMno$~!GrBPDMqA1BBi3$*Q@@cH>A)mMM=rfVCj2z_ffaY_3?2umc`Gn?&Dv_$L$Rn zvW{RUhhH<+eAZ6xeb;`CHvlm#USCgabPHxD4lVjf#JhVZ#gmc6<7P`M`XlTIlf~y# z7Wa@m4vT#Z+Y>J*HR|-dwDljOGs?aw9S|I;XQN!N}~;U?{H(Bg3ky5A5XgM zo1cYK(x3oD9D7V|v5b)L`fSu;Lb}7c3lBsq8B?L`02E0Xf1~dx0QN#P0cDv<{DdD5 z%O6p2?|0U}4nusVz{RbITRc-CZD&mw;D~Oy3{rrqTycF1n>;-sr+5@6&W94zk6~D- zW*j3D#SJ(K=PqzFn%v`gB0vdepC6yKigpl(6+7paZgv*=pEx~X<9Em9SK~kucnmgZCIHPy-{iMS9NQSqZo%pH=zD)ZPJ$5uub+@g0{mgz~ zj|ZW3`u^df>+*Q&JzL8=Z_n>BJJvdOGTEO46l!fu?mZr6Km9zv8%DlL7E(a?D`{-ASv1kb{;F2lIRc;({)G&Mq<= ziyA#OPWrAtmeJflS)qs~=-xQ!E5;6&-?y^TeSyqA8m1Dldlh4k_&DW8V8?%MdGqbzngpU2X4tW)2 zv6ho3$`dy!@%l?dla;y*noxqJ>+*QwIXT5M5*Th`ibtPM$~R1~+wS#Qj$8eC3zU>n zf6IvwBgHy`LZlh$^B7b$-sSQ%R`z=)l{`jCjkKU=OE()Ox83(-tBw#tLo4DWfEI(7Fo1sP%<&MvN>rK_g16V*PDq}}JOB1?4NG&_wj zC6=cI{_(P13X-Y$;lb+o{r&B)D2KzxvM=3Vx614*0;Td_PQP1^uSaV2oVl?m-i>!R%-AD~3J%Z^Zu8!Vch|JtmkTdsCk8`O z>aUtL)|f0=RfqMB-U8NBmzPF9e27Ts%KA}zof9}HwdG`zb&Wf|LH%-&QMqYWnQ`Ig zH*_##TuPwo5YdPkWZ}kLxK`_Y1jK-<1nQiV3!l0>4O;uf`s-yj*G#r15V^!vF@Z=X ziR*<2m#Ug}p-)71qFg_d;KF<8)!dD;XJE&0P4@(4Vf(k`yKi1;oiVh8s$DR*Ju`1W zPN7vv89x)FSx(?ncnem}C&wB7vIQS&XMp$1&yK?mb9I#4TiX?ETMJ@s`zt16*U;fTI1w!eZzOX zbmAGPG#lBTW71m@43KOsLWy0Nj}4q+X;3jS*-6dWY>&In^jV8o4@YSH%rtebw2jv} z>&Q!qkpS%P^L<0l|J|+6W*h(e{(xYKxa=scfs;g3QA2NmVRqt&l+SS3?cd%$e4cpf zf0*v@E}MRz)8=$IeSmB{l@KoyE2d!VMwK(oc4O5loMDnJEZY0q-GNNh(QMd+IU;e| z0Td2I-F|+jZ}|THl2-iA$g?e(^LTp^?KZ%|UQgJY!q*jG1W|4xbGlT>-fh@VUc@40 z;ZrUqz9d|G8Y4GZ>bM9K9;BBeRx+Z;kRBu&O|gPQkKA4AaYd{4LQ9i zCi+BAlheIx-mk%&xnT{j-_dc8h@WQ;}siHP?|SeBcL0|c4S;s`C+5Kbydxxxe^ z1UcWHaIe53w}l_KgNak8SA5+LmwLbp*FOGu?Qb~%uAFBlyv8Xv1i4A&EZO?8i!Z$NHJ$zy9HdQlq$9~>F$H1+ku#2TJ zy)i$a9t8=7@)hO7!N!hX!%61UL0!0NL|KPG+>UR&j%Sg?;cEMPpwYfr19?*%dDh~^ zY}8lR!O<(>WA27N)y|9W47pg+Z}Glcp%9vLAm{iCvSNJt#Z8zMHRkk=vPv7h!S;8C z8iXh}AiO{2TPju^S&jCDk%%kE%1wN=Nc2oVtXsX09;P zY*raKluE;4TX=S?2wOB=1+Ga4I4aL}$6f;JHsQZDIV+8x2(Y6ImnXC2Zf#H8 zA9`Nu$kzHEBnvFb=yS=w*6KQ1Z8z!W5lVKS^UI8w$=fA4dN>OK9`Y@`dN^C`J$gD@ z)osMg@36HEj&roIOvmz$)q|{BJfE4?eqXi(UWm=QzpmiRm?}F>nuvI_vVYql7sV%h zJ$}ZH7Vdjz&1xB* zcJBQix=YzkV`ZvsIrB*2SEAL`n_Sxw8r~(CXbFdB;;nl2nTP1TH#>TnCHD&h$#%`_ zI1bUPT@L=0#|RtyZZ9*}8rDL^>BL^x(-t}dR?gE&)*Cen!DhQTW8Ih*hJkmAISMzX zo`aWE6MT_0gPO(3bp;u@%Z|5)eXZ9NJ~k(P_FvR6I4q(ufhRLo$jo30i7DrowaW~7 zmKQlgpBPQXciSNvBpptR^EUgWshBD+)82R}lrK1X7K=66s<^JiXz`n9^^R9=-H>6) z%CJ0cUXViA+a#0cj7V$=4-Fv@;-KphH2csWH zJ@lkLJ+9qp-I1i2I8R`vxITfu(%BfPuBtn{;6Ke)vY01RZ7+OeN`7-LSh6h?MR)R1 zXG^Rin=y1b>hqq+0Un@k4%cQKRc&IIM`CVv23L4 zlow2)!yP#4?P|Vut{$JmZ!I-gSyjlbF4+Y*i(T5gwu}kfq;%V0`3;gk>px-nBh^eY zU5WL4XxNW(wo#tlS#h!3&Cnbl3szUmMo|5Ti8qeveJu8JZ2Z?#gw`m3QhAObAhMDPx`{BO37k{;sZnYK8sJ~S4 z51vxb0Z`N(0OcyaPx=ncHlP+k*pfiPvoCl1iON6HDdBrTV!T|@xmacMvS~XpMLYLY zIm3J2P)apMP#T5}(EGLApJuDVR9z$a(45>@LZ!nXlBs&r)lJ?#tS9(~thlA)pu$A3eLK{vQ(9lB0 zWM~VGl_gE7KD{!|uT5X;syY!oqe}Ru6vS1Vhu;)@hEZ&HQ<~9#W3wZs?Q}Mq@vuyK z`a;ML1BExkfcdqef|>ff?q~zjkuYxniF|PAo>;JrhxzU>#@EDI%Vuq*etfk7@Z$HN z@;M%e6nhJnp>NnqLB)&j5ip?jttAzdN#;-v;RaI!J^i4wy8VZ3jugI~bwABQ2=|Gi zWKvrk4{SR@kvLQq=H5;aibXk!dQl5}ci%noCvf?F2C^ zT0fLwBwl?L4x=qY%4Sj6o4^m8AD_^8#s`btR(abDD%f*;|#DA_}xMVGP5MxAarY zMolF{)yr>6=jn<%?UjEfeo1&6nGD$$shO`TzZ)U!vDO*)tqASa`gx>Dwf_7(q-hDE6~ z@e3;`VUf=MHMVYxBxM^jA*xKTtLdcO!xpmUjzCATlnuI{+qR1Nn6oWqSfel5%$A&2 zsot;AWL3@s;l*B8Dh0T{5&lFk#Y#=9uRG1EO&+5LX+M>nt(_X2qE+)2}P;Wi}c=m2c-*0?;=$|1f};P z2vVi^1<$$Xp1a<6t#93LefPhctR&B#J@eZ$znMMBvsXej)fLHzX^8;<0GYCqoEGNO z2=kI6#Kyc+rv&c-0KkYBI(ld=xEr&Rv!jKz9g-RS+zH8ybhowu0NiK3rRuxx2*=9( zstPm4UW1EJk8Cm=9-Pxs=foc#LU?$5w(@{lxbiW+T?8lrO^aXizZic=dKSc%hn7ry zb*}g9xs$d>uXFBsbiQlXym!JgG&TQq>IX;bu;EBayQRB##hJ_HdGk5-!r2CWfdH^mIuHA2COxpCb3Bj@QPI`Nt>3BjpF$5k?{qOje{ga8 z+L|@4ruxd8&P4dTHw9ltdCut?Cm7uwne?DF;Eryw9D1&@Z$Z-TncIv-QDeW%-$x1GIWQjXC6!m-Gg6TI`dkF|l%UOIz704fg&WsgPGK$V zDkl4Cr>B>^p50K?#hw6XU?2DsHpE|a zDly6ScL%AM**I|T8gIp|*Di{gx|Z<-%h(xg;PUj!UB&CY&&iYt<1Qej_%xw4!qS)S zJbae~=s|QhHCBbMVB(|7o1ETMo7V-OW^5c=@uURmzN(Z})Xk;T2sp3r4&$}ll|)8R37Zs{@{DJTYt_nsSe`IL?3V$ zWxD=UoG^x8t{w^Ht{Ff2A*9t*=_wacQ_vjjFSC#^W58 zVxL*6#K;zBR-ewQfV=D}!Eh3pv9BR_yU_q}#eoZpE*<_XZbU4!34HYC;@_{U4WyE}rL81k}cr{#K!6yK` z(u78ny|Vz9la}2wZ+|q!HT=GuDFUK*Bp5cL!7*(Tp+$XmI>r^p$ONBAd?fj^G%5Ml zz>~tdMW;{8Z2Y_5CoB8PkCR#d_i6l?)B0ORk+uwWD&pZmr}4QemSMjjZxR9FhO2`>V9PjhNiE}uRr*$*&ZzupeT^gDj=EG) z6*sS*cO>Vo7(H@a=wYvQgrxuaYBu=i=c;`&>JkMF(%1e!eeZ+)$$RWPvxWkzgXW!A zhK1S@8Z%y*d6J79lf(Fd6wok6DYjGZIFYQqGiQRpbhi0tmj_d)+uatgL!xlfiI{mU zS89Q0B90~GdS`1tlrwVQlcx{3(Fy0~qh4(hhxsH6`nQctpOkQaaGtM|exPBZy~qK{ zH&O{?e4-fPs^DIzpYv|6KQMI4@cUF?*Obq+ExU(0U8lO?Lvx1KQc>wAGq#D|y-Ig) zJh2g8kwHh0fa505TZ!>So!Lp7^0SOAmU5{+T}=Yi_I#>KUiIIf{4uuSxT5XgK+wcR zBx3UbTA{*M5&a}GuWX-nqIoZPMURbu>3Lq>IMBm!0=p^vhXbC`QVEs6`0LB+<8Q45 z;e?Fu%;q}3zf}5h14KTa4|<`lDQU3H+Q-*)0b0G#!RmUb+w<0c01Zs0G~j}Z+~}Riq!3-^wts>`O?*QkpXA%Klb-0sO!c1XO$JfqGq2e=OZLji>nOqVZj5h zDNLwd>M!`RRmdy_f7#UZSY2vmpxN2&h*3zmyAGhY-M&`s^rY-i zZkqs$7b8=udyO4ZRI)wPpzPezOgcOK}J9T5m~Rh4-Y)4LN)JA0x6)wJl; zbj!kfZLqQb#Q*gmrFd*TNgpMw3ETxSu)-JqQqM`DfS_eT^89J>^~JdI3^CQ zpOaF&*kMrF%Nau{RFrt+D^byY7UgLfdQJP31-thn!=@J=E90fjsRWh-MQ*~^)Wda& z4H^&i&o9_Y)C-nknA`1$wZ!@MGHWRp@6`xG8prpGb$)91-4V4(jd4!4%BtbEiTC13 zv*TAa_^HJQzgM7^lrJrKgrW||SybBP7E#~zub(}ZK@7hq`(+PLt5d`DlTHE%t^|%9qGuZsPC-X@=s1 zhdWzac=^=0;m+c3&qrGNZKAywAs$g|eL8jbwhnfF@Rb;9($dmOKbOM)eq8WEVmEkV z{R0a(2fHaLL9A3s$IPJOkX60%k zvAca6XT+KLW1`u5o65cMHcM2wR1Pa+{l09zNCI-mj;@e;!nykbKfY_E*F8CW?wgjl z!i`THwGsHpQ-}SW)#2Mdd=jmyqmu5e+oQ5^bU7i)jlkada?(ll##q&C(sm*d zM5hi*jeB!dHej)O0f6=yi&EVUZf}%scy#Xai~vs3K9WvJp+)pI)_!^~@{^sK1o=j+EmG{TRbmaS=5nWfaLvM%-vMTevN`ErRO za3;j>x+#&H;X1w=4F#l_>!lX&1!X>-G3O#ox{!Y?D{p!vxC`I+%V42gC;QMp>ruv~ zsp$SWjI8>K=oKGo0kZGV_jwmvbJ{+XsX;o|_1-RtGrihmM%T;8p7oDn^c||JY`{23 zQ1Pp<1sR5+L9P4wgvHcLAa3|9U$i^``(p#$kcXZ9fuY_etwje42Gq(CbkX&mCkYN= z*m=~&A^jn!w%ttlDHq+F<&3;7ytdJl11Lxs7pO*SnZsh_;GeIaK)cWGBu6>!YyIUs znj4pLinMT4(GWa03%BbW}=-~kMsAU^6?X?c2yunngkT)Tuq+w|PV z;%E++nr>#gvc#xpDm?Oa+hgdJoyZ3JE~`Br@N1T08Sk7uo^02?6*u{&xmYuXPa-Le zj8B?BBZ5_oLDYYN`u;ETTmGG_{mh<1bzLK=gUVA9lEqzTD#;t)g1_nD5xnwqQXILc zzZWq83CI&~VL%w8z0CG2SqbMA9`9jq2N~Rr{qfmj$K|51&dqe3uOe_P?6Y@Xo#e>^ zaq?fUJqLzm_&Yoq-x{q@US?KqPLjAA5g9z&Rn{4%Ygl7Vqd@KN5FS09jiWj20wbR$ z&%aFMe6|UVb6upvLVum}B##SBd!fSg0R>&sfB1IHLVphHl#1+X^w%&JD>Jp%l5D6T zWQ`ne+vE5F%kaZI?6V znNKtvan)>={Yj)i8Z(@~fvY4j1g;ttr30VnrtPb$0pSZ*zS9-_;zjL6p1Bb6t#;3T ztDpdgXg?Pj02S!vk2p0~!-TbDoOaLgU$ohOK z>5oBcIOaR%g5tGS%c`VLxt*~63~)F$fFcz-$&Kq;_cC7T-+z`NH%+Yc-PS*<2fuQ* zcF2sKXpR`nRqVP^+;Sk6wRFR?(5(r-PU8E0r%_(Wo6ie`R$28H%c;1&xL97I9hTGs z4=VWy?vz;WfCAWdgeRZknqKZtbE{D%esGo!M?`vM*SrbnF2$jq_{7|ykh6p9sRv5M zwbinPZ^dBugsr>ckb90in0~!aPtEn#;4&vppTcvlFH|rkRTZg z6QyotJfyZ6mvZsPqo5BsiS1Z+1h+eXXC4#n^$3rmZ9OiH;1M%v@ zMpT=bNrE`hQf}bNPx8ivVnYUZOQtYIR5iZt3Axy|4zbTC+=bIogw)is8GSO-CTV`I z(JN1GHK}H$RlhI74x+kbNtW-@bGV^}df#YX45XdX(>{1~4icsexl5ZAP!zazHr^61 z)@}h-JZ)@IjA1h}^z-@(lRfpXG;emcCkimh1d>wkdSzozl@E zm@CLWB53?HTNew@3hNb{7FQvD;q7l59j4K;qd?B}+cC?;Qx%olFKeq3yhFKcI2fA; z^qn#a?pU(*Oc5|tc092rQi)CGZMk11OfY|+A%!p59%{`Y7nKh0dLPuDMqE6%?;5zI0Yi_hham9MQVgb^&bA9lpwWFr1acZ56ke> z-}eYCmXwN63$jz-`bRL@gF2@5@&pXmWUF`9WWOA1>Zjnb1zIpBLUYhFB3LR?y5AbN zO#JQ)>>ZFX)3nBup1u4fkSSy5I$^4~wM$*e#T;xv9ao!6_|{#LnaqWa%e1IK_6;+n z`C_wphp2yr$RkLXv?*(Jh5$@jdml=ohi%t*Q~Hq}C#{gX6Kp)AGV++@oC}}Umwg|2 zQwBb7FHTZ2V9K$bAGZTyNdTl9#w7+oX-sK)9t}^_o8%>5lH#XINjwUFKzh4zM!?9y zqBBa@D3|^YV3Y>ztNSe#>1)_F;lEm4tg-0Eya-!P^O&J1zkb(4@U zBW^cSyt`$jL~%eEDNWXMi&WKy#i_m^p+<0qo?EcT$LgX;o$8)d2u@kpFC*fcp|bTd zQJX=BPI@eLx^q0S!8}{^2U% z(9Q5tIq8dbyb3ka%t$unj;xpW&r9stOqd4p)cwa#PEn#`5n(F^8S zcPPc%_x+r`#(oc*{!NY1CieloI)J?EW)88r`f=^?jFNiwXDY-b=@i}*bPn)FCrKmm z!=Nka)DSLP@|Aq-F*M2Rf}k`JM$tQ;sqPzmSGh!&X8PEuF{ud> z4-g+8%Tp{?;CXR+wBs9LAtoXBs8WR`g^}GGA8$=|Inqkrzpg zx%X4I!{z1a3g5RBy3Hi@D(2>-7JFufa6^_er0Kq{r3!F)IKQ+#I_+aUEUabm^@5Jg z@|`CSNOHCfIm~E`pX_Z<_-wY2MJvwT=H*2d=WAOZA|SX!as6`gfGWbvFrR@#*%z8*O1oT9uQxT_N^VbF)? z=7g!TVSY-H4&}k=ha6RA?yjwA#0keyB^!&UK{fMUsclUQCYKj`Qor8gY$|7-`H;c* zv>$Z(FaQ8p{?@Xxn#!`W|2UY$97m>m#fU3)NzwP1X!o$*CU_7+Xi=H(&QI_XRc0K=opG$NkD^ccc-sWZYPXx zdw`rr@YG2dmQ|B|VH^=S&G5E|F$;45p#phQ3UW|ci@0@pn-QyZuE#y`SEHn2R_a@7 za`I5i6!=UxUH5oeeaG`G;ZBum6;E|BhmUxnOpUMIFM1m{s@nzFEI7n9=vnTBGnAu2 zJSnvMz-Ky>=mDx~#mmYOQKJ=#}qfKJW(3wKncQ)O}h%~@kYtG+J{v)zWa zE9#=g37BP{7C89&%bJ!eO>ax=-EcWR-b>N-kqi}cuH1A+sRIBv46QNe#CmF~q6kNO zKDfD~8IsT4-U)M#3;>8px;w!Uwn#Ly8Pd|)L7a8(T^lR2wYfO!6POxE%}Ew%Wv%qW z8L9n3T?g^P79nEJDk(uM<}Qi>ut%cd%;LRck;nZ=yVEkw2C6#j(3jKo>3&}b)7ettJMH$FE3K1XLu zez1s$2tNqI4}tJvG40YYJA^snFMB5!XS?6wm?QX+c1U}SDhjhI_+OS(R94gc%i@{>OKW?l-&Pp1 z|3wpRZShaC{>8WJncw03-4TrWU%3CG{g2pxD`T|O)I{YR5iZy6Da(noUav1|?uf89 z7yW$+Hxm&Of*_#0P;;<3FBA+G=7pOhMR;N6aC373Bv=G4DEv1lWd{@*?tnmELt()A ztT8wcga||kVqw86A|M3eg+eS4yuub>%n$;Gnt?G0<`C%LAT*q$ZeYi0%#ud{$5j28^z{hx&K|K~#lgJ5tF7!2bD zBQU)S3O0lA!a*p;MwP?)HI5Dy4^T@!zX zD8_%?7XOh{G5-IN_CFN<)<-ea{2{~iXH2{2|Eu5s$=P*h{SQ9=EVuvQ3K;1BF7j{j z`(L{LOV_`}z`v#Z-|G4=UH=vX|CaK9tLy(8UBv&qV?;V&e(1Sj?gyE^D|KS-CGpK3 zD#`(VT|cv1isCR5A}1w%6aYX%dHuoyq^472gal}1HF<&!Y(gOK-Lv|kZj3}!o=O3{s#^4h<=^AT!23k{wXR^KUivvmJl`E2KC*Md%ncjhth z+srb;`|3E&@k$^BjK2&1(awiW6-Lq%2ni;5a-k{6a+6U-=0=ZOL~?U1q}}RgfWa9o z4+t;M0;vpr(axr>^p@$!6~_q!Y;z8ldM!_q_##}&WVAHzyA$7jC4u1NxM44nsH~bP zAr_<7pQy==_o}1Re>_;_wx3ACJ=ZS+3Cy{WNehoOIe-*#SO?OZoBg%-_gpFek)7ks zv#XVUrji_SZc0{BKTlTbw3l2QG9XLr++;%OZd^I=E%Q~P(%#4jABZ3QC^frfn*DzJO_F;ksJB}A>~L(K@?8*tk7R`w z#%t8rkycn_knDQyc29a_`fx;1Z$-qX-G)Fiu^uh{dyfehx2B$$?5|MFJp{0D3oi?W z)CN4MgY~>qtaqHtO4B5{R>R9QQ{+d9v41&?$F2h98@HWN1dN_4xfXNA&+mT9(8y+PXKs-S82LZwwtr&LE!$COdqc+=hX%WM9V?-Y)oPyM#TP!G}Opij`He z43;mY5=bks_QLY3GC7XX3t@yn(crztA@TSN!``)qu3MQWw}?xX*n4@HBra*B159uD zf+O!$4^Wc?T)MS6ZKFAF0Y-MB9`s}`QIoCfv7KegSSzz8_t`Q&bshV5)a>S()kD1k z83>}%C6;h7HCfu*TiPnN5hwy>uSXXFx9~ceEEA30Q5pUxmamXnaYwd(S`pONL?%DK zFJ;XOJ9+k1s;onb2KP4*oJAAmgu-mt0NEC{=Jtmw*r(Puuw1=hoxpE9xTW zFS6!;`i1i_kA&P(;2kkmm2>u9k1xGzY`Hf`z4Psq&G~V|!ZXK+B5Qvz64Qu@w#`(lJ3)F@?gS`#`HW9`A4scI^UmeXk_hdP0tF6u#ixU z5T&n`zp7Tf7Y;ILh3_MnX4&vb3k$Cl_AiFHyIS4{^4tN6Zd7%@yXjNA__8jl6=j07 z2jpxT27!afW%>_?Woo~Q;63;h!l1Faxy$eE^yU1^6XTklwW8M*r6emSOf2o6{Rjc( z0tZHu+QYF$z~m1xudmQ%X1!%PGe5TXBTn85Uww`9A)7e?TCQo$r7u15VtDf_+60er zOKswSY%w7rQu3tzrsA4pk=<69!&aE+2kuFkp_jvKh9=*1P}<-_oe?K5ZQeEdD{2BC zF|kUaN4x2r`V`iV5vy6$Yw=nrrdLkgUk*Pc&ic5W7<Sxt0`aB-1)2yh=}+w&If{B>p7nFbHw9*X08 zcao}%u@G__%RNM=xZwF9_qPi0H;tn4JKm*0oi5(OYHB_q~5MBQdw;+0>aAK*@F$_?tOToq;MNKYSTm>qZgDjwkA>Dkr7 zKi3;B2FPDpKHG+yA~KvLP%0j__f7b-6pHv@u)3byECob?O|9|puQQ%`j^#XjP0+#P z=ki&;wBTSp9>$;68Y_|Z}1a`ww8ouiLr1lmt6UC z-rU>Sd_dmD?p}~VYhRMC)k|v+4!@9|`Dzy4ll#p25cVtXXU#i=iCbT*R)oJRj=LCXl;<=hDA8HW5{{C6oM!lT~kJ*xXzD+WoV8!DywX%Y-(#lF*$-sX2RM9!}n zc3jiAtKA^E8b>F3gy-4?w2eKqbRn3wH>tnm9%u0#(Aa4hAGGqSW9%52PGRbL-YU7; z_Ck2?P27Ejtp2*iN9oMig3sc94P60Q7r?Q0O|8S&1A%Wp;UzupKFiC@c3L|EcBSW{ zs`8Q!R)O4HnH1+XiBI?%dCeBN0nuvx8Q-chB=_h)$JJ`PDiZGO%@n?}L8U}v9{{G5Zl6jWXplS5iknwNw!S#K9Pq#`sn>>+n R%=a!pSzcZ4wT!9X{{b0Bnlk_Z literal 0 HcmV?d00001 diff --git a/ports/fxcg50/icon-unsNWUSB.png b/ports/fxcg50/icon-unsNWUSB.png new file mode 100644 index 0000000000000000000000000000000000000000..e2aaf60da8917eb1a390252c913699ab80393961 GIT binary patch literal 12678 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 KEY_EXE ) + return mp_obj_new_bool(false); + + int translatedKey = KeyTranslationMap[ key ]; + + if (translatedKey==-1) + return mp_obj_new_bool(false); + clearevents(); - bool down = keydown(key) != 0; + + bool down = keydown(translatedKey) != 0; return mp_obj_new_bool(down); } @@ -115,7 +141,8 @@ STATIC const mp_rom_map_elem_t ion_module_globals_table[] = { OBJ(__init__), /*Numworks keycodes */ - + /* BE CAREFUL THERE ARE MISSING SLOTS */ + INT(KEY_LEFT), // value 0 INT(KEY_UP), INT(KEY_DOWN), @@ -124,7 +151,7 @@ STATIC const mp_rom_map_elem_t ion_module_globals_table[] = { INT(KEY_BACK), INT(KEY_HOME), INT(KEY_ONOFF), // value 7 - + INT(KEY_SHIFT), // value 12 INT(KEY_ALPHA), INT(KEY_XNT), @@ -147,18 +174,21 @@ STATIC const mp_rom_map_elem_t ion_module_globals_table[] = { INT(KEY_EIGHT), INT(KEY_NINE), INT(KEY_LEFTPARENTHESIS), - INT(KEY_RIGHTPARENTHESIS), - INT(KEY_FOUR), + INT(KEY_RIGHTPARENTHESIS), // value 34 + + INT(KEY_FOUR), // value 36 INT(KEY_FIVE), INT(KEY_SIX), INT(KEY_MULTIPLICATION), - INT(KEY_DIVISION), - INT(KEY_ONE), + INT(KEY_DIVISION), // value 40 + + INT(KEY_ONE), // value 42 INT(KEY_TWO), INT(KEY_THREE), INT(KEY_PLUS), - INT(KEY_MINUS), - INT(KEY_ZERO), + INT(KEY_MINUS), // value 46 + + INT(KEY_ZERO), // value 48 INT(KEY_DOT), INT(KEY_EE), INT(KEY_ANS), From 6225b1d66745bf4a15cb01940f96241c54691dde Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Sat, 10 Feb 2024 10:39:24 +0100 Subject: [PATCH 22/27] added more examples using Kandinsky/Ion/Time modules from NW + reorganisation of NW examples --- .../examples/numworks/NW_pacman_original.py | 223 ++++++++++++ ports/sh/examples/numworks/NW_snake_origin.py | 303 ++++++++++++++++ .../{ => numworks}/cg_NW_kand_mandel.py | 0 .../{ => numworks}/cg_NW_kand_test.py | 0 ports/sh/examples/numworks/cg_NW_pacman.py | 227 ++++++++++++ .../examples/{ => numworks}/cg_NW_rosace.py | 0 ports/sh/examples/numworks/cg_NW_snake.py | 326 ++++++++++++++++++ .../{ => numworks}/cg_NW_time_test.py | 0 8 files changed, 1079 insertions(+) create mode 100644 ports/sh/examples/numworks/NW_pacman_original.py create mode 100644 ports/sh/examples/numworks/NW_snake_origin.py rename ports/sh/examples/{ => numworks}/cg_NW_kand_mandel.py (100%) rename ports/sh/examples/{ => numworks}/cg_NW_kand_test.py (100%) create mode 100644 ports/sh/examples/numworks/cg_NW_pacman.py rename ports/sh/examples/{ => numworks}/cg_NW_rosace.py (100%) create mode 100644 ports/sh/examples/numworks/cg_NW_snake.py rename ports/sh/examples/{ => numworks}/cg_NW_time_test.py (100%) diff --git a/ports/sh/examples/numworks/NW_pacman_original.py b/ports/sh/examples/numworks/NW_pacman_original.py new file mode 100644 index 000000000..52af608c0 --- /dev/null +++ b/ports/sh/examples/numworks/NW_pacman_original.py @@ -0,0 +1,223 @@ +# PacMan par Kevin FEDYNA +# https://nsi.xyz/numapps/pac-man-en-python-numworks/ + +from kandinsky import fill_rect, draw_string +from ion import keydown +from math import sqrt +from random import randint +from time import monotonic + +try: + from kandinsky import get_keys + color = (192, 53, 53) +except: + color = (255, 183, 52) + +terrain = (262143,131841,187245,187245,131073,186285,135969,252783,249903,251823,1152,251823,249903,251823,131841,187245,147465,219051,135969,195453,131073,262143) +bits = 18 +width = 320 +height = 222 +colors = ((0, 0, 0), (32, 48, 248), (248, 224, 8), tuple(color)) +ghost_color = ((255, 184, 255), (255, 0,0), (255, 184, 82), (0, 255, 255)) +pacgommes = [0,130302,9360,74898,131070,75858,126174,8208,8208,8208,8208,8208,8208,8208,130302,74898,49140,43092,126174,66690,131070,0] +superpacgommes = [0,0,65538,0,0,0,0,0,0,0,0,0,0,0,0,0,65538,0,0,0,0,0,0] +frightened = 0 +lives = 2 +won = 0 +lvl = 0 +score = 0 +chained = 0 + + +class Entity: + def __init__(self, x, y, clr, d=0): + self.x = x + self.y = y + self.d = d + self.nd = d + self.f = 0 + self.out = 0 + self.color = clr + fill_rect(int(self.x*10)+136,int(self.y*10)-3,8,8,self.color) + def espace(self,dx=-1,dy=-1): + if dx == dy: + dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[self.nd] + return not terrain[int(self.y + 5.5*dy)]>>(bits-1-int(self.x + 5.5*dx)) & 1 and ((dx != 0 and self.y%1 == 0.5) or (dy != 0 and self.x%1== 0.5)) + def move(self): + global frightened, ghosts, score, chained, lives, total, won + dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[self.d] + if self.espace(dx,dy): + fill_rect(int(self.x*10)+136,int(self.y*10)-3,8,8,colors[0]) + self.x = (round(self.x + dx, 1) - 0.5) % 16.5 + 0.5 + self.y = round(self.y + dy, 1) + fill_rect(int(self.x*10)+136,int(self.y*10)-3,8,8,self.color) + if self.color == colors[2]: + if pacgommes[int(self.y)] >> (bits - 1 - int(self.x)) & 1: + pacgommes[int(self.y)] -= 1 << (bits - 1 - int(self.x)) + score += 10 + if superpacgommes[int(self.y)] >> (bits - 1 - int(self.x)) & 1: + superpacgommes[int(self.y)] -= 1 << (bits - 1 - int(self.x)) + score += 50 + chained = 0 + frightened = monotonic() + for g in ghosts: + if g.out: + g.color = colors[1] + g.d = (3, 2, 1, 0)[g.d] + g.f = 1 + for g in range(4): + if sqrt((self.x-ghosts[g].x)**2+(self.y-ghosts[g].y)**2) < 0.6: + if ghosts[g].f: + chained += 1 + total += 1 + score += (1 << chained)*100 + ghosts[g].f = 0 + ghosts[g].color = ghost_color[g] + ghosts[g].x = 9 + ghosts[g].y = 8.5 + if total == 16: + score += 12000 + else: + for gp in range(4): + ghosts[gp].f = 0 + ghosts[gp].color = ghost_color[gp] + ghosts[gp].x = 9 + ghosts[gp].y = 10.5 + ghosts[gp].out = 0 + self.x = 9 + self.y = 16.5 + self.d, self.nd = 0, 0 + lives -= 1 + return render() + if not won and score > 10000: + lives += 1 + won = 1 + px, py = int(self.x - 5.5*dx), int(self.y - 5.5*dy) + if pacgommes[py]>>(bits-1-px) & 1: + fill_rect(px*10+144,py*10+5,2,2,(250, 207, 173)) + if superpacgommes[py]>>(bits-1-px) & 1: + fill_rect(px*10+143,py*10+4,4,4,(250, 207, 173)) + def ia(self,x,y): + if self.f: + while True: + d = randint(0,3) + dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[d] + if d != (3,2,1,0)[self.d] and self.espace(dx,dy): + self.d = d + break + else: + distances = [9999 for _ in range(4)] + for i in range(4): + if i != (3,2,1,0)[self.d]: + dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[i] + if self.espace(dx,dy): + distances[i] = sqrt((self.y + dy - y)**2 + (self.x + dx - x)**2) + self.d = distances.index(min(distances)) + +def prebuild(): + fill_rect(0,0,width,height,colors[0]) + fill_rect(138, 0, 2, height, colors[3]) + draw_string("PAC-MAN", 35, 10, colors[3], colors[0]) + draw_string("nsi.xyz/pacman", 0, 204,colors[0], colors[3]) + draw_string("Score :", 35, 40, (255,)*3, colors[0]) + draw_string("Niveau :", 30, 90, (255,)*3, colors[0]) + +def render(): + global terrain, pacgommes, superpacgommes, lives, arrivee + if lives == -1: + return 42 + draw_string(str(lvl),70-5*len(str(lvl)),110,(255,)*3,colors[0]) + fill_rect(0,150,138,20,colors[0]) + for i in range(lives): + fill_rect(60-(lives-1)*20+i*40,150,20,20,colors[2]) + for l in range(len(terrain)): + for c in range(bits): + fill_rect(c*10+140,l*10+1,10,10,colors[0]) + if pacgommes[l]>>(bits-1-c) & 1: + fill_rect(c*10+144,l*10+5,2,2,(250, 207, 173)) + if superpacgommes[l]>>(bits-1-c) & 1: + fill_rect(c*10+143,l*10+4,4,4,(250, 207, 173)) + if terrain[l]>>(bits-1-c) & 1: + for d in ((1,0),(0,1),(-1,0),(0,-1)): + if 0 <= l + d[0] <= len(terrain) - 1 and 0 <= c + d[1] <= bits - 1 and not terrain[l + d[0]]>>(bits-1-(c+d[1])) & 1: + fill_rect(c*10+140+9*(d[1]==1),l*10+1+9*(d[0]==1),1+9*(d[1]==0),1+9*(d[0]==0),colors[1]) + arrivee = monotonic() + +def engine(): + global frightened, ghosts, pacgommes, superpacgommes, lvl, arrivee, total + while True: + pacgommes = [0,130302,9360,74898,131070,75858,126174,8208,8208,8208,8208,8208,8208,8208,130302,74898,49140,43092,126174,66690,131070,0] + superpacgommes = [0,0,65538,0,0,0,0,0,0,0,0,0,0,0,0,0,65538,0,0,0,0,0,0] + lvl += 1 + total = 0 + render() + pacman = Entity(9, 16.5, colors[2]) + ghosts = [Entity(9, 10.5, ghost_color[i]) for i in range(4)] + while sum(pacgommes) + sum(superpacgommes): + depart = monotonic() + for i in range(4): + if keydown(i): + if i == (3,2,1,0)[pacman.d]: + pacman.d = i + pacman.nd = i + while monotonic() - depart < 0.01: + if pacman.espace(): + pacman.d = pacman.nd + if pacman.move() == 42: + draw_string("GAME OVER",185,100,colors[3],colors[0]) + return 69 + + draw_string(str(score),70-5*len(str(score)),60,(255,)*3,colors[0]) + + """ Fantomes """ + + if frightened: + if monotonic() - frightened > 6.5: + for g in ghosts: + if g.f: + g.color = (255,)*3 + if monotonic() - frightened > 8.5: + frightened = 0 + for g in range(4): + ghosts[g].color = ghost_color[g] + ghosts[g].f = 0 + + if arrivee: + if monotonic() - arrivee > 0 and not ghosts[1].out: + ghosts[1].out = 1 + ghosts[1].y = 8.5 + if monotonic() - arrivee > 2.5 and not ghosts[0].out: + ghosts[0].out = 1 + ghosts[0].y = 8.5 + if monotonic() - arrivee > 5 and not ghosts[3].out: + ghosts[3].out = 1 + ghosts[3].y = 8.5 + if monotonic() - arrivee > 7.5 and not ghosts[2].out: + ghosts[2].out = 1 + ghosts[2].y = 8.5 + fill_rect(220,101,20,10,colors[0]) + arrivee = 0 + + pdx, pdy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[pacman.d] + + # Pinky + ghosts[0].ia(pacman.x + 20 * pdx, pacman.y + 20 * pdy) + ghosts[0].move() + + # Inky + ghosts[3].ia(max(min(ghosts[1].x + 2*(pacman.x + 20 * pdx - ghosts[1].x), 16.5), 1.5), max(min(ghosts[1].y +2*(pacman.y + 20 * pdy - ghosts[1].y), 21.5), 1.5)) + ghosts[3].move() + + # Blinky + ghosts[1].ia(pacman.x, pacman.y) + ghosts[1].move() + + # Clyde + if sqrt((ghosts[2].x - pacman.x)**2 + (ghosts[2].y - pacman.y)**2) > 4: + ghosts[2].ia(pacman.x, pacman.y) + else: + ghosts[2].ia(1.5, 20.5) + ghosts[2].move() + +prebuild() +engine() diff --git a/ports/sh/examples/numworks/NW_snake_origin.py b/ports/sh/examples/numworks/NW_snake_origin.py new file mode 100644 index 000000000..93621dd3e --- /dev/null +++ b/ports/sh/examples/numworks/NW_snake_origin.py @@ -0,0 +1,303 @@ +# Snake from Golem64 +# https://my.numworks.com/python/golem64/snake + +#Version 1.7 STABLE +#Tip: You should try to press +#some keys in the menu... +from random import * +from kandinsky import * +from ion import * +from time import * +#from pomme import * +def oscolor(): + try: + get_keys() + except: + return 'orange' + else: + return 'red' +def lastPos(i,x,y): + if i[-1]==3: + pos=[x-10,y] + elif i[-1]==2: + pos=[x,y-10] + elif i[-1]==0: + pos=[x+10,y] + elif i[-1]==1: + pos=[x,y+10] + pos[0],pos[1]=checkTeleport(pos[0],pos[1]) + return pos +def newApple(appleC,bgC): + applex=randint(0,31)*10+4 + appley=randint(0,21)*10+5 + while get_pixel(applex,appley)!=bgC: + applex=randint(0,31)*10+4 + appley=randint(0,21)*10+5 + fill_rect(applex-4,appley-4,10,10,appleC) + return applex,appley +def checkTeleport(x,y): + if x<4: + x=314 + if x>314: + x=4 + if y<5: + y=215 + if y>215: + y=5 + return x,y +def getMove(u): + for k in range(4): + if keydown(k)==True and u+k!=3: return k + return u +def clearDraw(): fill_rect(0,0,320,222,(255,255,255)) +def clearHome(): print("\n \n \n \n \n \n \n \n \n \n \n \n \n ") +def redraw(): + draw_string("(DELETE to exit)",0,0) + printLetter([1,1,1,1,0,0,1,1,1,0,0,1,1,1,1],70,80,10,(0,204,0)) + fill_rect(95,80,2,4,(0,0,0)) + fill_rect(95,86,2,4,(0,0,0)) + fill_rect(100,84,4,2,(255,0,0)) + fill_rect(104,82,2,2,(255,0,0)) + fill_rect(104,86,2,2,(255,0,0)) + printLetter([1,1,1,1,0,1,1,0,1,1,0,1,1,0,1],110,80,10,(0,0,0)) + printLetter([1,1,1,1,0,1,1,1,1,1,0,1,1,0,1],150,80,10,(0,0,0)) + printLetter([1,0,1,1,0,1,1,1,0,1,0,1,1,0,1],190,80,10,(0,0,0)) + printLetter([1,1,1,1,0,0,1,1,1,1,0,0,1,1,1],230,80,10,(0,0,0)) +def printLetter(letter,x,y,size,color): + for yi in range(5): + for xi in range(3): + if letter[yi*3+xi]==1: + fill_rect(x+(xi*size),y+(yi*size),size,size,color) +def menu(): + clearDraw() + printLetter([1,1,1,1,0,1,1,0,1,1,0,1,1,0,1],110,80,10,(0,0,0)) + printLetter([1,1,1,1,0,1,1,1,1,1,0,1,1,0,1],150,80,10,(0,0,0)) + printLetter([1,0,1,1,0,1,1,1,0,1,0,1,1,0,1],190,80,10,(0,0,0)) + printLetter([1,1,1,1,0,0,1,1,1,1,0,0,1,1,1],230,80,10,(0,0,0)) + anim=[1,1,1,1,1,1,1,1,1,4,4,3,3,4,4,1,1] + ax=0 + ay=120 + aendx=-110 + aendy=120 + u=1 + aback=0 + for i in range(len(anim)): + ax=ax+((anim[i]==1)-(anim[i]==3))*10 + ay=ay+((anim[i]==2)-(anim[i]==4))*10 + if aendx<0: + aendx=aendx+10 + else: + aendx=aendx+((anim[i-11]==1)-(anim[i-11]==3))*10 + aendy=aendy+((anim[i-11]==2)-(anim[i-11]==4))*10 + fill_rect(aendx,aendy,10,10,(255,255,255)) + fill_rect(ax,ay,10,10,(0,204,0)) +# aback=lastPos(anim,ax,ay) +# if u==26 or u==24: +# fill_rect(ax-1,ay-1,3,1,(0,0,0)) +# fill_rect(ax-1,ay+1,3,1,(0,0,0)) +# fill_rect(aback[0],aback[1],10,10,(0,204,0)) +# elif u==34 or u==25: +# fill_rect(ax-1,ay-1,1,3,(0,0,0)) +# fill_rect(ax+1,ay-1,1,3,(0,0,0)) +# fill_rect(aback[0]-2,aback[1]-2,5,5,(0,204,0)) + sleep(0.05) + fill_rect(ax+5,ay,2,4,(0,0,0)) + fill_rect(ax+5,ay+6,2,4,(0,0,0)) + fill_rect(ax+10,ay+4,4,2,(255,0,0)) + fill_rect(ax+14,ay+2,2,2,(255,0,0)) + fill_rect(ax+14,ay+6,2,2,(255,0,0)) + draw_string("(DELETE to exit)",0,0) + draw_string("> Play <",125,140,oscolor()) + draw_string(" Options ",110,165) + darkMode=0 + Speed=0.05 + power=5 + score=1 + exit=0 + sel=1 + while keydown(KEY_OK)!=True and exit==0: + if keydown(KEY_DOWN) and sel==1: + draw_string(" Play ",125,140) + draw_string("> Options <",110,165,oscolor()) + sel=2 + elif keydown(KEY_UP) and sel==2: + draw_string("> Play <",125,140,oscolor()) + draw_string(" Options ",110,165) + sel=1 + if keydown(KEY_LEFTPARENTHESIS) and keydown(KEY_RIGHTPARENTHESIS): + draw_string("Dark mode enabled !",80,195) + darkMode=1 + if keydown(KEY_BACKSPACE): + exit=1 + sleep(0.1) + if sel==2 and exit!=1: + fill_rect(0,130,300,60,(255,255,255)) + Speed=0.05 + power=5 + score=1 + draw_string("Speed:"+str(Speed),50,140,oscolor(),'white') + draw_string("Power:+"+str(power),200,140) + draw_string("Score:+"+str(score),50,170) + draw_string("Play",220,170) + sel=1 + sleep(0.2) + while keydown(KEY_OK)!=True or sel!=4: + if keydown(KEY_RIGHT): + sel=sel+1 + elif keydown(KEY_DOWN): + sel=sel+2 + elif keydown(KEY_LEFT): + sel=sel-1 + elif keydown(KEY_UP): + sel=sel-2 + if sel<0: + sel=0 + if sel>4: + sel=4 + if sel==1: + draw_string("Speed:"+str(Speed),50,140,oscolor(),'white') + draw_string("Power:+"+str(power),200,140) + draw_string("Score:+"+str(score),50,170) + draw_string("Play",220,170) + if keydown(KEY_OK): + clearHome() + Speed=input("Speed:") + redraw() + elif sel==2: + draw_string("Speed:"+str(Speed),50,140) + draw_string("Power:+"+str(power),200,140,oscolor(),'white') + draw_string("Score:+"+str(score),50,170) + draw_string("Play",220,170) + if keydown(KEY_OK): + clearHome() + power=int(input("Power:+")) + redraw() + elif sel==3: + draw_string("Speed:"+str(Speed),50,140) + draw_string("Power:+"+str(power),200,140) + draw_string("Score:+"+str(score),50,170,oscolor(),'white') + draw_string("Play",220,170) + if keydown(KEY_OK): + clearHome() + score=int(input("Score:")) + redraw() + elif sel==4: + draw_string("Speed:"+str(Speed),50,140) + draw_string("Power:+"+str(power),200,140) + draw_string("Score:+"+str(score),50,170) + draw_string("Play",220,170,oscolor(),'white') + if (keydown(KEY_LEFTPARENTHESIS) and keydown(KEY_RIGHTPARENTHESIS)) or darkMode==1: + draw_string("Dark mode enabled !",80,195) + darkMode=1 + if keydown(KEY_BACKSPACE): + exit=1 + break + sleep(0.1) + if exit!=1: + if darkMode==1: + launch(1,Speed,power,score) + elif darkMode==0: + launch(0,Speed,power,score) + elif exit==1: + clearDraw() + return +def launch(darkmode=0,speed=0.05,applePower=5,appleScore=1): + bgC=(248,252,248) + borderC=(0,0,0) + snakeC=(0,204,0) + appleC=(248,0,0) + if darkmode==1: + bgC=(0,0,0) + borderC=(0,0,204) + fill_rect(0,0,320,222,bgC) +# fill_rect(315,0,5,222,borderC) +# fill_rect(0,0,5,222,borderC) +# fill_rect(0,0,320,1,(197,52,49)) + fill_rect(0,221,320,1,(0,0,0)) + try: + get_keys() + except: + fill_rect(0,0,320,1,(255,181,49)) + else: + fill_rect(0,0,320,1,(197,52,49)) + snake=[3,3,3,3,3] + x=154 + y=115 + endx=104 + endy=115 + u,v=3,3 + length=5 + applex,appley=newApple(appleC,bgC) + score,touched=0,0 + while touched!=borderC and touched!=snakeC: + if keydown(0) or keydown(1) or keydown(2) or keydown(3): + u=getMove(u) + if keydown(KEY_BACKSPACE): + while keydown(KEY_BACKSPACE): + sleep(0.1) + while keydown(KEY_BACKSPACE)!=True: + sleep(0.1) + while keydown(KEY_BACKSPACE): + sleep(0.1) + snake.append(u) + if x==applex and y==appley: + length=length+float(applePower) + applex,appley=newApple(appleC,bgC) + score=score+int(appleScore) + x=x+((u==3)-(u==0))*10 + y=y+((u==2)-(u==1))*10 + x,y=checkTeleport(x,y) + if length: + length=length-1 + else: + snake.remove(snake[0]) + endx=endx+((v==3)-(v==0))*10 + endy=endy+((v==2)-(v==1))*10 + endx,endy=checkTeleport(endx,endy) + v=snake[0] + fill_rect(endx-4,endy-4,10,10,bgC) + touched=get_pixel(x,y) + if x<0 or x>320 or y<0 or y>220: + touched=borderC + if touched!=appleC and touched!=bgC: + touched=borderC + fill_rect(x-4,y-4,10,10,snakeC) + back=lastPos(snake,x,y) + if u==3 or u==0: + fill_rect(x,y-4,2,4,(0,0,0)) + fill_rect(x,y+2,2,4,(0,0,0)) + fill_rect(back[0]-4,back[1]-4,10,10,snakeC) + elif u==2 or u==1: + fill_rect(x-4,y,4,2,(0,0,0)) + fill_rect(x+2,y,4,2,(0,0,0)) + fill_rect(back[0]-4,back[1]-4,10,10,snakeC) + sleep(float(speed)) +# EPILEPSY WARNING !!! +# snakeC=(randint(0,255),randint(0,255),randint(0,255)) + while snakeC==appleC or snakeC==bgC: + snakeC=(randint(0,255),randint(0,255),randint(0,255)) + # beau() + if len(snake)==640: + if darkmode==1: + draw_string("You win !",120,100,'white','black') + draw_string("(You reached the max length)",20,120,'white','black') + else: + draw_string("You win !",120,100) + draw_string("(You reached the max length)",20,120) + touched=borderC + if darkmode==1: + draw_string("Score:"+str(score),10,10,'white','black') + draw_string("(OK=play again, DELETE=Menu)",10,30,'white','black') + else: + draw_string("Score:"+str(score),10,10) + draw_string("(OK=play again, DELETE=Menu)",10,30) + choice=0 + while choice==0: + if keydown(KEY_OK): + choice=1 + launch(darkmode,speed,applePower,appleScore) + elif keydown(KEY_BACKSPACE): + choice=2 + menu() + print("Score:",score) +menu() diff --git a/ports/sh/examples/cg_NW_kand_mandel.py b/ports/sh/examples/numworks/cg_NW_kand_mandel.py similarity index 100% rename from ports/sh/examples/cg_NW_kand_mandel.py rename to ports/sh/examples/numworks/cg_NW_kand_mandel.py diff --git a/ports/sh/examples/cg_NW_kand_test.py b/ports/sh/examples/numworks/cg_NW_kand_test.py similarity index 100% rename from ports/sh/examples/cg_NW_kand_test.py rename to ports/sh/examples/numworks/cg_NW_kand_test.py diff --git a/ports/sh/examples/numworks/cg_NW_pacman.py b/ports/sh/examples/numworks/cg_NW_pacman.py new file mode 100644 index 000000000..8b7e422b9 --- /dev/null +++ b/ports/sh/examples/numworks/cg_NW_pacman.py @@ -0,0 +1,227 @@ +# PacMan par Kevin FEDYNA +# https://nsi.xyz/numapps/pac-man-en-python-numworks/ +# converti sur fxCG50 et PythonExtra par SlyVTT + +from kandinsky import fill_rect, draw_string +from ion import keydown, KEY_LEFT, KEY_UP, KEY_DOWN, KEY_RIGHT +from math import sqrt +from random import randint +from time import monotonic + +try: + from kandinsky import get_keys + color = (192, 53, 53) +except: + color = (255, 183, 52) + +terrain = (262143,131841,187245,187245,131073,186285,135969,252783,249903,251823,1152,251823,249903,251823,131841,187245,147465,219051,135969,195453,131073,262143) +bits = 18 +width = 320 +height = 222 +colors = ((0, 0, 0), (32, 48, 248), (248, 224, 8), tuple(color)) +ghost_color = ((255, 184, 255), (255, 0,0), (255, 184, 82), (0, 255, 255)) +pacgommes = [0,130302,9360,74898,131070,75858,126174,8208,8208,8208,8208,8208,8208,8208,130302,74898,49140,43092,126174,66690,131070,0] +superpacgommes = [0,0,65538,0,0,0,0,0,0,0,0,0,0,0,0,0,65538,0,0,0,0,0,0] +frightened = 0 +lives = 2 +won = 0 +lvl = 0 +score = 0 +chained = 0 + + +class Entity: + def __init__(self, x, y, clr, d=0): + self.x = x + self.y = y + self.d = d + self.nd = d + self.f = 0 + self.out = 0 + self.color = clr + fill_rect(int(self.x*10)+136,int(self.y*10)-3,8,8,self.color) + def espace(self,dx=-1,dy=-1): + if dx == dy: + dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[self.nd] + return not terrain[int(self.y + 5.5*dy)]>>(bits-1-int(self.x + 5.5*dx)) & 1 and ((dx != 0 and self.y%1 == 0.5) or (dy != 0 and self.x%1== 0.5)) + def move(self): + global frightened, ghosts, score, chained, lives, total, won + dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[self.d] + if self.espace(dx,dy): + fill_rect(int(self.x*10)+136,int(self.y*10)-3,8,8,colors[0]) + self.x = (round(self.x + dx, 1) - 0.5) % 16.5 + 0.5 + self.y = round(self.y + dy, 1) + fill_rect(int(self.x*10)+136,int(self.y*10)-3,8,8,self.color) + if self.color == colors[2]: + if pacgommes[int(self.y)] >> (bits - 1 - int(self.x)) & 1: + pacgommes[int(self.y)] -= 1 << (bits - 1 - int(self.x)) + score += 10 + if superpacgommes[int(self.y)] >> (bits - 1 - int(self.x)) & 1: + superpacgommes[int(self.y)] -= 1 << (bits - 1 - int(self.x)) + score += 50 + chained = 0 + frightened = monotonic() + for g in ghosts: + if g.out: + g.color = colors[1] + g.d = (3, 2, 1, 0)[g.d] + g.f = 1 + for g in range(4): + if sqrt((self.x-ghosts[g].x)**2+(self.y-ghosts[g].y)**2) < 0.6: + if ghosts[g].f: + chained += 1 + total += 1 + score += (1 << chained)*100 + ghosts[g].f = 0 + ghosts[g].color = ghost_color[g] + ghosts[g].x = 9 + ghosts[g].y = 8.5 + if total == 16: + score += 12000 + else: + for gp in range(4): + ghosts[gp].f = 0 + ghosts[gp].color = ghost_color[gp] + ghosts[gp].x = 9 + ghosts[gp].y = 10.5 + ghosts[gp].out = 0 + self.x = 9 + self.y = 16.5 + self.d, self.nd = 0, 0 + lives -= 1 + return render() + if not won and score > 10000: + lives += 1 + won = 1 + px, py = int(self.x - 5.5*dx), int(self.y - 5.5*dy) + if pacgommes[py]>>(bits-1-px) & 1: + fill_rect(px*10+144,py*10+5,2,2,(250, 207, 173)) + if superpacgommes[py]>>(bits-1-px) & 1: + fill_rect(px*10+143,py*10+4,4,4,(250, 207, 173)) + def ia(self,x,y): + if self.f: + while True: + d = randint(0,3) + dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[d] + if d != (3,2,1,0)[self.d] and self.espace(dx,dy): + self.d = d + break + else: + distances = [9999 for _ in range(4)] + for i in range(4): + if i != (3,2,1,0)[self.d]: + dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[i] + if self.espace(dx,dy): + distances[i] = sqrt((self.y + dy - y)**2 + (self.x + dx - x)**2) + self.d = distances.index(min(distances)) + +def prebuild(): + fill_rect(0,0,width,height,colors[0]) + fill_rect(138, 0, 2, height, colors[3]) + draw_string("PAC-MAN", 35, 10, colors[3], colors[0]) + draw_string("nsi.xyz/pacman", 0, 204,colors[0], colors[3]) + draw_string("Score :", 35, 40, (255,)*3, colors[0]) + draw_string("Niveau :", 30, 90, (255,)*3, colors[0]) + +def render(): + global terrain, pacgommes, superpacgommes, lives, arrivee + if lives == -1: + return 42 + draw_string(str(lvl),70-5*len(str(lvl)),110,(255,)*3,colors[0]) + fill_rect(0,150,138,20,colors[0]) + for i in range(lives): + fill_rect(60-(lives-1)*20+i*40,150,20,20,colors[2]) + for l in range(len(terrain)): + for c in range(bits): + fill_rect(c*10+140,l*10+1,10,10,colors[0]) + if pacgommes[l]>>(bits-1-c) & 1: + fill_rect(c*10+144,l*10+5,2,2,(250, 207, 173)) + if superpacgommes[l]>>(bits-1-c) & 1: + fill_rect(c*10+143,l*10+4,4,4,(250, 207, 173)) + if terrain[l]>>(bits-1-c) & 1: + for d in ((1,0),(0,1),(-1,0),(0,-1)): + if 0 <= l + d[0] <= len(terrain) - 1 and 0 <= c + d[1] <= bits - 1 and not terrain[l + d[0]]>>(bits-1-(c+d[1])) & 1: + fill_rect(c*10+140+9*(d[1]==1),l*10+1+9*(d[0]==1),1+9*(d[1]==0),1+9*(d[0]==0),colors[1]) + arrivee = monotonic() + +def engine(): + global frightened, ghosts, pacgommes, superpacgommes, lvl, arrivee, total + while True: + pacgommes = [0,130302,9360,74898,131070,75858,126174,8208,8208,8208,8208,8208,8208,8208,130302,74898,49140,43092,126174,66690,131070,0] + superpacgommes = [0,0,65538,0,0,0,0,0,0,0,0,0,0,0,0,0,65538,0,0,0,0,0,0] + lvl += 1 + total = 0 + render() + pacman = Entity(9, 16.5, colors[2]) + ghosts = [Entity(9, 10.5, ghost_color[i]) for i in range(4)] + while sum(pacgommes) + sum(superpacgommes): + depart = monotonic() + lk=[KEY_LEFT, KEY_UP, KEY_DOWN, KEY_RIGHT] + for i in range(4): + if keydown(lk[i]): + if i == (3,2,1,0)[pacman.d]: + pacman.d = i + pacman.nd = i + while monotonic() - depart < 0.01: + if pacman.espace(): + pacman.d = pacman.nd + if pacman.move() == 42: + draw_string("GAME OVER",185,100,colors[3],colors[0]) + return 69 + + draw_string(str(score),70-5*len(str(score)),60,(255,)*3,colors[0]) + + """ Fantomes """ + + if frightened: + if monotonic() - frightened > 6.5: + for g in ghosts: + if g.f: + g.color = (255,)*3 + if monotonic() - frightened > 8.5: + frightened = 0 + for g in range(4): + ghosts[g].color = ghost_color[g] + ghosts[g].f = 0 + + if arrivee: + if monotonic() - arrivee > 0 and not ghosts[1].out: + ghosts[1].out = 1 + ghosts[1].y = 8.5 + if monotonic() - arrivee > 2.5 and not ghosts[0].out: + ghosts[0].out = 1 + ghosts[0].y = 8.5 + if monotonic() - arrivee > 5 and not ghosts[3].out: + ghosts[3].out = 1 + ghosts[3].y = 8.5 + if monotonic() - arrivee > 7.5 and not ghosts[2].out: + ghosts[2].out = 1 + ghosts[2].y = 8.5 + fill_rect(220,101,20,10,colors[0]) + arrivee = 0 + + pdx, pdy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[pacman.d] + + # Pinky + ghosts[0].ia(pacman.x + 20 * pdx, pacman.y + 20 * pdy) + ghosts[0].move() + + # Inky + ghosts[3].ia(max(min(ghosts[1].x + 2*(pacman.x + 20 * pdx - ghosts[1].x), 16.5), 1.5), max(min(ghosts[1].y +2*(pacman.y + 20 * pdy - ghosts[1].y), 21.5), 1.5)) + ghosts[3].move() + + # Blinky + ghosts[1].ia(pacman.x, pacman.y) + ghosts[1].move() + + # Clyde + if sqrt((ghosts[2].x - pacman.x)**2 + (ghosts[2].y - pacman.y)**2) > 4: + ghosts[2].ia(pacman.x, pacman.y) + else: + ghosts[2].ia(1.5, 20.5) + ghosts[2].move() + + +fill_rect(0,0,319,219,"black") +prebuild() +engine() diff --git a/ports/sh/examples/cg_NW_rosace.py b/ports/sh/examples/numworks/cg_NW_rosace.py similarity index 100% rename from ports/sh/examples/cg_NW_rosace.py rename to ports/sh/examples/numworks/cg_NW_rosace.py diff --git a/ports/sh/examples/numworks/cg_NW_snake.py b/ports/sh/examples/numworks/cg_NW_snake.py new file mode 100644 index 000000000..a498aa897 --- /dev/null +++ b/ports/sh/examples/numworks/cg_NW_snake.py @@ -0,0 +1,326 @@ +# Snake from Golem64 +# https://my.numworks.com/python/golem64/snake +# +# converted to PythonExtra on fx-CG50 by SlyVTT +# +# Version 1.7 STABLE +# Tip: You should try to press +# some keys in the menu... + +from random import * +from kandinsky import * +from ion import * +from time import * + +def oscolor(): + try: + get_keys() + except: + return 'orange' + else: + return 'red' + +def lastPos(i,x,y): + if i[-1]==3: + pos=[x-10,y] + elif i[-1]==2: + pos=[x,y-10] + elif i[-1]==0: + pos=[x+10,y] + elif i[-1]==1: + pos=[x,y+10] + pos[0],pos[1]=checkTeleport(pos[0],pos[1]) + return pos + +def newApple(appleC,bgC): + applex=randint(0,31)*10+4 + appley=randint(0,21)*10+5 + while get_pixel(applex,appley)!=bgC: + applex=randint(0,31)*10+4 + appley=randint(0,21)*10+5 + fill_rect(applex-4,appley-4,10,10,appleC) + return applex,appley + +def checkTeleport(x,y): + if x<4: + x=314 + if x>314: + x=4 + if y<5: + y=215 + if y>215: + y=5 + return x,y + +def getMove(u): + lk=[KEY_LEFT, KEY_UP, KEY_DOWN, KEY_RIGHT] + for k in range(4): + if keydown(lk[k])==True and u+k!=3: return k + return u + +def clearDraw(): + fill_rect(0,0,320,222,(255,255,255)) + +def clearHome(): + print("\n \n \n \n \n \n \n \n \n \n \n \n \n ") + +def redraw(): + draw_string("(DELETE to exit)",0,0) + printLetter([1,1,1,1,0,0,1,1,1,0,0,1,1,1,1],70,80,10,(0,204,0)) + fill_rect(95,80,2,4,(0,0,0)) + fill_rect(95,86,2,4,(0,0,0)) + fill_rect(100,84,4,2,(255,0,0)) + fill_rect(104,82,2,2,(255,0,0)) + fill_rect(104,86,2,2,(255,0,0)) + printLetter([1,1,1,1,0,1,1,0,1,1,0,1,1,0,1],110,80,10,(0,0,0)) + printLetter([1,1,1,1,0,1,1,1,1,1,0,1,1,0,1],150,80,10,(0,0,0)) + printLetter([1,0,1,1,0,1,1,1,0,1,0,1,1,0,1],190,80,10,(0,0,0)) + printLetter([1,1,1,1,0,0,1,1,1,1,0,0,1,1,1],230,80,10,(0,0,0)) + +def printLetter(letter,x,y,size,color): + for yi in range(5): + for xi in range(3): + if letter[yi*3+xi]==1: + fill_rect(x+(xi*size),y+(yi*size),size,size,color) + +def menu(): + clearDraw() + printLetter([1,1,1,1,0,1,1,0,1,1,0,1,1,0,1],110,80,10,(0,0,0)) + printLetter([1,1,1,1,0,1,1,1,1,1,0,1,1,0,1],150,80,10,(0,0,0)) + printLetter([1,0,1,1,0,1,1,1,0,1,0,1,1,0,1],190,80,10,(0,0,0)) + printLetter([1,1,1,1,0,0,1,1,1,1,0,0,1,1,1],230,80,10,(0,0,0)) + anim=[1,1,1,1,1,1,1,1,1,4,4,3,3,4,4,1,1] + ax=0 + ay=120 + aendx=-110 + aendy=120 + u=1 + aback=0 + for i in range(len(anim)): + ax=ax+((anim[i]==1)-(anim[i]==3))*10 + ay=ay+((anim[i]==2)-(anim[i]==4))*10 + if aendx<0: + aendx=aendx+10 + else: + aendx=aendx+((anim[i-11]==1)-(anim[i-11]==3))*10 + aendy=aendy+((anim[i-11]==2)-(anim[i-11]==4))*10 + fill_rect(aendx,aendy,10,10,(255,255,255)) + fill_rect(ax,ay,10,10,(0,204,0)) +# aback=lastPos(anim,ax,ay) +# if u==26 or u==24: +# fill_rect(ax-1,ay-1,3,1,(0,0,0)) +# fill_rect(ax-1,ay+1,3,1,(0,0,0)) +# fill_rect(aback[0],aback[1],10,10,(0,204,0)) +# elif u==34 or u==25: +# fill_rect(ax-1,ay-1,1,3,(0,0,0)) +# fill_rect(ax+1,ay-1,1,3,(0,0,0)) +# fill_rect(aback[0]-2,aback[1]-2,5,5,(0,204,0)) + sleep(0.05) + fill_rect(ax+5,ay,2,4,(0,0,0)) + fill_rect(ax+5,ay+6,2,4,(0,0,0)) + fill_rect(ax+10,ay+4,4,2,(255,0,0)) + fill_rect(ax+14,ay+2,2,2,(255,0,0)) + fill_rect(ax+14,ay+6,2,2,(255,0,0)) + draw_string("(DELETE to exit)",0,0) + draw_string("> Play <",125,140,oscolor()) + draw_string(" Options ",110,165) + darkMode=0 + Speed=0.05 + power=5 + score=1 + exit=0 + sel=1 + while keydown(KEY_OK)!=True and exit==0: + if keydown(KEY_DOWN) and sel==1: + draw_string(" Play ",125,140) + draw_string("> Options <",110,165,oscolor()) + sel=2 + elif keydown(KEY_UP) and sel==2: + draw_string("> Play <",125,140,oscolor()) + draw_string(" Options ",110,165) + sel=1 + if keydown(KEY_LEFTPARENTHESIS) and keydown(KEY_RIGHTPARENTHESIS): + draw_string("Dark mode enabled !",80,195) + darkMode=1 + if keydown(KEY_BACKSPACE): + exit=1 + sleep(0.1) + if sel==2 and exit!=1: + fill_rect(0,130,300,60,(255,255,255)) + Speed=0.05 + power=5 + score=1 + draw_string("Speed:"+str(Speed),50,140,oscolor(),'white') + draw_string("Power:+"+str(power),200,140) + draw_string("Score:+"+str(score),50,170) + draw_string("Play",220,170) + sel=1 + sleep(0.2) + while keydown(KEY_OK)!=True or sel!=4: + if keydown(KEY_RIGHT): + sel=sel+1 + elif keydown(KEY_DOWN): + sel=sel+2 + elif keydown(KEY_LEFT): + sel=sel-1 + elif keydown(KEY_UP): + sel=sel-2 + if sel<0: + sel=0 + if sel>4: + sel=4 + if sel==1: + draw_string("Speed:"+str(Speed),50,140,oscolor(),'white') + draw_string("Power:+"+str(power),200,140) + draw_string("Score:+"+str(score),50,170) + draw_string("Play",220,170) + if keydown(KEY_OK): + clearHome() + Speed=input("Speed:") + redraw() + elif sel==2: + draw_string("Speed:"+str(Speed),50,140) + draw_string("Power:+"+str(power),200,140,oscolor(),'white') + draw_string("Score:+"+str(score),50,170) + draw_string("Play",220,170) + if keydown(KEY_OK): + clearHome() + power=int(input("Power:+")) + redraw() + elif sel==3: + draw_string("Speed:"+str(Speed),50,140) + draw_string("Power:+"+str(power),200,140) + draw_string("Score:+"+str(score),50,170,oscolor(),'white') + draw_string("Play",220,170) + if keydown(KEY_OK): + clearHome() + score=int(input("Score:")) + redraw() + elif sel==4: + draw_string("Speed:"+str(Speed),50,140) + draw_string("Power:+"+str(power),200,140) + draw_string("Score:+"+str(score),50,170) + draw_string("Play",220,170,oscolor(),'white') + if (keydown(KEY_LEFTPARENTHESIS) and keydown(KEY_RIGHTPARENTHESIS)) or darkMode==1: + draw_string("Dark mode enabled !",80,195) + darkMode=1 + if keydown(KEY_BACKSPACE): + exit=1 + break + sleep(0.1) + if exit!=1: + if darkMode==1: + launch(1,Speed,power,score) + elif darkMode==0: + launch(0,Speed,power,score) + elif exit==1: + clearDraw() + return + +def launch(darkmode=0,speed=0.05,applePower=5,appleScore=1): + bgC=(248,252,248) + borderC=(0,0,0) + snakeC=(0,204,0) + appleC=(248,0,0) + if darkmode==1: + bgC=(0,0,0) + borderC=(0,0,204) + fill_rect(0,0,320,222,bgC) +# fill_rect(315,0,5,222,borderC) +# fill_rect(0,0,5,222,borderC) +# fill_rect(0,0,320,1,(197,52,49)) + fill_rect(0,221,320,1,(0,0,0)) + try: + get_keys() + except: + fill_rect(0,0,320,1,(255,181,49)) + else: + fill_rect(0,0,320,1,(197,52,49)) + snake=[3,3,3,3,3] + x=154 + y=115 + endx=104 + endy=115 + u,v=3,3 + length=5 + applex,appley=newApple(appleC,bgC) + score,touched=0,0 + while touched!=borderC and touched!=snakeC: + if keydown(KEY_LEFT) or keydown(KEY_UP) or keydown(KEY_DOWN) or keydown(KEY_RIGHT): + u=getMove(u) + if keydown(KEY_BACKSPACE): + while keydown(KEY_BACKSPACE): + sleep(0.1) + while keydown(KEY_BACKSPACE)!=True: + sleep(0.1) + while keydown(KEY_BACKSPACE): + sleep(0.1) + snake.append(u) + if x==applex and y==appley: + length=length+float(applePower) + applex,appley=newApple(appleC,bgC) + score=score+int(appleScore) + x=x+((u==3)-(u==0))*10 + y=y+((u==2)-(u==1))*10 + x,y=checkTeleport(x,y) + if length: + length=length-1 + else: + snake.remove(snake[0]) + endx=endx+((v==3)-(v==0))*10 + endy=endy+((v==2)-(v==1))*10 + endx,endy=checkTeleport(endx,endy) + v=snake[0] + fill_rect(endx-4,endy-4,10,10,bgC) + touched=get_pixel(x,y) + if x<0 or x>320 or y<0 or y>220: + touched=borderC + if touched!=appleC and touched!=bgC: + touched=borderC + fill_rect(x-4,y-4,10,10,snakeC) + back=lastPos(snake,x,y) + if u==3 or u==0: + fill_rect(x,y-4,2,4,(0,0,0)) + fill_rect(x,y+2,2,4,(0,0,0)) + fill_rect(back[0]-4,back[1]-4,10,10,snakeC) + elif u==2 or u==1: + fill_rect(x-4,y,4,2,(0,0,0)) + fill_rect(x+2,y,4,2,(0,0,0)) + fill_rect(back[0]-4,back[1]-4,10,10,snakeC) + sleep(float(speed)) +# EPILEPSY WARNING !!! +# snakeC=(randint(0,255),randint(0,255),randint(0,255)) + while snakeC==appleC or snakeC==bgC: + snakeC=(randint(0,255),randint(0,255),randint(0,255)) + # beau() + if len(snake)==640: + if darkmode==1: + draw_string("You win !",120,100,'white','black') + draw_string("(You reached the max length)",20,120,'white','black') + else: + draw_string("You win !",120,100) + draw_string("(You reached the max length)",20,120) + touched=borderC + if darkmode==1: + draw_string("Score:"+str(score),10,10,'white','black') + draw_string("(OK=play again, DELETE=Menu)",10,30,'white','black') + else: + draw_string("Score:"+str(score),10,10) + draw_string("(OK=play again, DELETE=Menu)",10,30) + choice=0 + while choice==0: + if keydown(KEY_OK): + choice=1 + launch(darkmode,speed,applePower,appleScore) + elif keydown(KEY_BACKSPACE): + choice=2 + menu() + print("Score:",score) + + +try: + CGEXT_Set_Margin_Color( "black" ) +except: + print("fxCG Extension not supported") + +menu() diff --git a/ports/sh/examples/cg_NW_time_test.py b/ports/sh/examples/numworks/cg_NW_time_test.py similarity index 100% rename from ports/sh/examples/cg_NW_time_test.py rename to ports/sh/examples/numworks/cg_NW_time_test.py From 26afb31ff21d25748f59360c52bfe5ad6d1ad954 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Tue, 13 Feb 2024 18:27:47 +0100 Subject: [PATCH 23/27] added some extra verification on get_pixel() after validation on actual hardware --- ports/fxcg50/Makefile | 2 +- ports/sh/numworks/modkandinsky.c | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ports/fxcg50/Makefile b/ports/fxcg50/Makefile index 69ae0d847..1d0e8f40f 100644 --- a/ports/fxcg50/Makefile +++ b/ports/fxcg50/Makefile @@ -10,7 +10,7 @@ SH_CONVFLAGS := --cg all: PythonExtra.g3a PythonExtra.g3a: $(BUILD)/firmware.bin icon-uns.png icon-sel.png - fxgxa --g3a -n PythonExtra --icon-uns=icon-uns.png --icon-sel=icon-sel.png $< -o $@ + fxgxa --g3a -n PyExtra_NW --icon-uns=icon-uns.png --icon-sel=icon-sel.png $< -o $@ send: all fxlink -sw PythonExtra.g3a diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index 79a71c913..63677d2ee 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -23,10 +23,12 @@ extern bool is_timered; extern unsigned int timer_altered[9]; extern bool is_refreshed_required; +#define NW_MAX_X 320 +#define NW_MAX_Y 222 + /* Parameters used in windowed mode to center the screen of the NW in the fxCG screen*/ -#define DELTAXNW \ - ((DWIDTH - 320) / 2) // we center the NW screen on Casio's screen -#define DELTAYNW 1 // NW screen will be cut in the bottom +#define DELTAXNW ((DWIDTH - NW_MAX_X) / 2) // we center the NW screen on Casio's screen +#define DELTAYNW ((DHEIGHT - NW_MAX_Y) / 2) /* refresh rate of the screen */ #define TARGET_FPS 20 @@ -196,11 +198,11 @@ static mp_obj_t Kandinsky_get_pixel(mp_obj_t _x, mp_obj_t _y) { int x = mp_obj_get_int(_x) + DELTAXNW; int y = mp_obj_get_int(_y) + DELTAYNW; - if (x >= 0 && x < DWIDTH && y >= 0 && y < DHEIGHT) { + if ((!is_dwindowed && x >= 0 && x < DWIDTH && y >= 0 && y < DHEIGHT) || (is_dwindowed && x >= 0 && x < NW_MAX_X && y >= 0 && y < NW_MAX_Y)) { color_t color = gint_vram[DWIDTH * y + x]; return Kandinsky_make_color(color); } - return mp_const_none; + return Kandinsky_make_color(0x0000); } static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { From 05bfce99d698664fd4d7b8e8518d346e09e0814b Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Tue, 13 Feb 2024 19:56:28 +0100 Subject: [PATCH 24/27] added a better mechanism for differentiating compilation of modules between FxCG50 and fx9860G --- ports/fx9860g3/Makefile | 1 + ports/fxcg50/Makefile | 1 + ports/sh/Makefile | 29 +++++++++++++++++++++++------ ports/sh/numworks/ion.c | 4 ---- ports/sh/numworks/modkandinsky.c | 4 ---- ports/sh/numworks/time.c | 4 ---- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/ports/fx9860g3/Makefile b/ports/fx9860g3/Makefile index a49a16024..a34f28323 100644 --- a/ports/fx9860g3/Makefile +++ b/ports/fx9860g3/Makefile @@ -1,6 +1,7 @@ include ../../py/mkenv.mk SH_CFLAGS := -DFX9860G +TARGETCASIO := "FX9860G" SH_LDFLAGS := -T fx9860g.ld -ljustui-fx -lm -lgint-fx -lc -lgint-fx -lgcc SH_ASSETS := \ diff --git a/ports/fxcg50/Makefile b/ports/fxcg50/Makefile index 1d0e8f40f..a527c3ff8 100644 --- a/ports/fxcg50/Makefile +++ b/ports/fxcg50/Makefile @@ -1,6 +1,7 @@ include ../../py/mkenv.mk SH_CFLAGS := -DFXCG50 +TARGETCASIO := "FXCG50" SH_LDFLAGS := -T fxcg50.ld -ljustui-cg -lm -lgint-cg -lc -lgint-cg -lgcc SH_ASSETS := img_modifier_states.png font_9.png font_13.png font_19.png PoliceNW.png diff --git a/ports/sh/Makefile b/ports/sh/Makefile index 06d049a53..ccaca7464 100644 --- a/ports/sh/Makefile +++ b/ports/sh/Makefile @@ -15,9 +15,6 @@ SRC_C = \ ports/sh/debug.c \ ports/sh/keymap.c \ ports/sh/modcasioplot.c \ - ports/sh/numworks/modkandinsky.c \ - ports/sh/numworks/ion.c \ - ports/sh/numworks/time.c \ ports/sh/modgint.c \ ports/sh/mphalport.c \ ports/sh/objgintimage.c \ @@ -30,13 +27,33 @@ SRC_C = \ SRC_QSTR += \ ports/sh/main.c \ ports/sh/modcasioplot.c \ - ports/sh/numworks/modkandinsky.c \ - ports/sh/numworks/ion.c \ - ports/sh/numworks/time.c \ ports/sh/modgint.c \ ports/sh/objgintimage.c \ ports/sh/pyexec.c \ + +ifeq ($(TARGETCASIO),"FXCG50") + $(info ************ FXCG50 VERSION ************ ) + $(info ********* Add Numworks modules ********* ) + SRC_C += \ + ports/sh/numworks/modkandinsky.c \ + ports/sh/numworks/ion.c \ + ports/sh/numworks/time.c \ + + SRC_QSTR += \ + ports/sh/numworks/modkandinsky.c \ + ports/sh/numworks/ion.c \ + ports/sh/numworks/time.c + + +endif + +ifeq ($(TARGETCASIO),"FX9860G") + $(info *********** FX9860G VERSION *********** ) + +endif + + ASSETS_O := $(SH_ASSETS:%=$(BUILD)/sh_assets/%.o) OBJ = $(PY_O) $(ASSETS_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) diff --git a/ports/sh/numworks/ion.c b/ports/sh/numworks/ion.c index 19aa6af7d..8cf09bd9d 100644 --- a/ports/sh/numworks/ion.c +++ b/ports/sh/numworks/ion.c @@ -1,5 +1,3 @@ -#ifdef FXCG50 - //---------------------------------------------------------------------------// // ____ PythonExtra // //.-'`_ o `;__, A community port of MicroPython for CASIO calculators. // @@ -204,5 +202,3 @@ const mp_obj_module_t ion_module = { }; MP_REGISTER_MODULE(MP_QSTR_ion, ion_module); - -#endif // FXCG50 \ No newline at end of file diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index 63677d2ee..c44593b89 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -1,5 +1,3 @@ -#ifdef FXCG50 - //---------------------------------------------------------------------------// // ____ PythonExtra // //.-'`_ o `;__, A community port of MicroPython for CASIO calculators. // @@ -320,5 +318,3 @@ const mp_obj_module_t kandinsky_module = { }; MP_REGISTER_MODULE(MP_QSTR_kandinsky, kandinsky_module); - -#endif //FXCG50 \ No newline at end of file diff --git a/ports/sh/numworks/time.c b/ports/sh/numworks/time.c index 157c1eaee..debcdd0ec 100644 --- a/ports/sh/numworks/time.c +++ b/ports/sh/numworks/time.c @@ -1,5 +1,3 @@ -#ifdef FXCG50 - //---------------------------------------------------------------------------// // ____ PythonExtra // //.-'`_ o `;__, A community port of MicroPython for CASIO calculators. // @@ -111,5 +109,3 @@ const mp_obj_module_t time_module = { }; MP_REGISTER_MODULE(MP_QSTR_time, time_module); - -#endif //FXCG50 \ No newline at end of file From 5f385d8bccb30d12c9c426619005c7e5bbeac264 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Sat, 24 Feb 2024 09:14:12 +0100 Subject: [PATCH 25/27] convert NW font to unicode and added main letters with accent - WIP --- ports/fxcg50/Makefile | 2 +- .../{PoliceNW.png => PoliceNW/U+0020.png} | Bin ports/fxcg50/PoliceNW/U+00A0.png | Bin 0 -> 1009 bytes ports/fxcg50/fxconv-metadata.txt | 4 ++-- ports/sh/mpconfigport.h | 5 +++++ ports/sh/numworks/modkandinsky.c | 19 ++++++++++++++---- 6 files changed, 23 insertions(+), 7 deletions(-) rename ports/fxcg50/{PoliceNW.png => PoliceNW/U+0020.png} (100%) create mode 100644 ports/fxcg50/PoliceNW/U+00A0.png diff --git a/ports/fxcg50/Makefile b/ports/fxcg50/Makefile index a527c3ff8..93dd1722f 100644 --- a/ports/fxcg50/Makefile +++ b/ports/fxcg50/Makefile @@ -4,7 +4,7 @@ SH_CFLAGS := -DFXCG50 TARGETCASIO := "FXCG50" SH_LDFLAGS := -T fxcg50.ld -ljustui-cg -lm -lgint-cg -lc -lgint-cg -lgcc -SH_ASSETS := img_modifier_states.png font_9.png font_13.png font_19.png PoliceNW.png +SH_ASSETS := img_modifier_states.png font_9.png font_13.png font_19.png PoliceNW SH_METADATA := fxconv-metadata.txt SH_CONVFLAGS := --cg diff --git a/ports/fxcg50/PoliceNW.png b/ports/fxcg50/PoliceNW/U+0020.png similarity index 100% rename from ports/fxcg50/PoliceNW.png rename to ports/fxcg50/PoliceNW/U+0020.png diff --git a/ports/fxcg50/PoliceNW/U+00A0.png b/ports/fxcg50/PoliceNW/U+00A0.png new file mode 100644 index 0000000000000000000000000000000000000000..8aed984155f966fdcc163f7a39d292dfbdc22e67 GIT binary patch literal 1009 zcmeAS@N?(olHy`uVBq!ia0vp^3xGI*gAGW|Qm>rJz`)p&>FgZf>FlgfP?VpRnUl)E zpfRy_qOHea2brVs!Aq4w9l5)#Qdanhxwy(~<#JsikkuL{>!bI#NsZUDt9$W-sDcm4 z8(%$mv}SelY6XtE1`BHo&0QXy|Gl|%lWZ;C9p7Euzq`C4K-cJOmNdJ`T{Gd0D`t4D z?`jO^c;T+U%TUGm^o+?%ETa0>SO54?CH^pS?fJi!yRP&cWti__bDHO}+C7DN_qOn! z`+Y=l+Vs#z#g7ap>v{Y+QF`>T`8mt!tn(y|CU;go64*E+J85ZWv4^|wclHo5BALARfpqO zF&WQa5-t_>gX6_bHobMyvo^8EyklVYZ_7Eg?#Xh^l+6czOV$QUz1Yoab$!phSHCT4 z875r`{ljKq!QxgtW$(R5cV*A+{ad~4-hF;I+oGv{1=gp5(ZZJG?e4b zCYGe8D3oWGWGJ|M`UZqI@`(eZ`=qCfV@SoVx3_Qm9dZz9yU6$OZ+zt*Cf~P-Sq*=; zuPaS0xcuPB;^kMaRlgS&*%`^i(!t=^z#%{~#n{9ksK6mWB2~kFBWiY3cDwn8@B8-W zmFHWpzx8f)$lCiab}gyf2XxYBbDzs5XXE1Te7F~r{r2bY`S$UVzkV&P-51YzNd4~g zRrL|s#RD5yVJ{-r`N4D+IOW?l5};tM9SL>`s~YzV_1dOXrw_^U9ap(<@S*b;sEN;@<}swVlq3KU?Gd>hqNR{aoK_ z7M5!A3T(S$|53O8$NTf*d2i>seNTEnXLm@$JpCnalvMuy+@WpbuI?b9z|^vZVf(z` z+5b2De7d84T{Ustq3HL_yLgTF>1BsM6)m35DKLjw38--5K8E$L6{hXK5qt<3^9-J@ KelF{r5}E+DA*i(g literal 0 HcmV?d00001 diff --git a/ports/fxcg50/fxconv-metadata.txt b/ports/fxcg50/fxconv-metadata.txt index c752d6b51..be1380ab6 100644 --- a/ports/fxcg50/fxconv-metadata.txt +++ b/ports/fxcg50/fxconv-metadata.txt @@ -30,10 +30,10 @@ font_19.png: grid.border: 0 proportional: true -PoliceNW.png: +PoliceNW: name: numworks type: font - charset: print + charset: unicode grid.size: 10x16 grid.padding: 0 grid.border: 0 diff --git a/ports/sh/mpconfigport.h b/ports/sh/mpconfigport.h index 57c291055..539a1330c 100644 --- a/ports/sh/mpconfigport.h +++ b/ports/sh/mpconfigport.h @@ -126,3 +126,8 @@ typedef long mp_off_t; #define MICROPY_HW_MCU_NAME "sh-4a" #define MP_STATE_PORT MP_STATE_VM + +/* Activate support of unicode string in MicroPython */ +#define MICROPY_PY_BUILTINS_STR_UNICODE (1) + + diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index c44593b89..443493c69 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -220,7 +220,7 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { } font_t const *old_font = dfont(&numworks); - + int u = 0; int v = 0; for (int l = 0; l < (int)text_len; l++) { @@ -228,10 +228,21 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { u = 0; v += 16; } else { - drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback); - dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, + /* The following test is for support of unicode characters that are encoded on 2 chars */ + /* This gives text[l]<0 and we need to pass 2 chars to dtext to take care of unicode encoding */ + if(text[l]>=0){ + drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback); + dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, &text[l], 1); - u += 10; + u += 10; + } else { + drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback); + dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, + &text[l], 2); + u += 10; + l++; + } + } } From 35b73ba95442b7ce47b964505c2368ce75fda9aa Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Sat, 24 Feb 2024 10:49:28 +0100 Subject: [PATCH 26/27] added better (ie. cleaner) support for unicode characters in Kandinsky module --- ports/sh/mpconfigport.h | 4 ---- ports/sh/numworks/modkandinsky.c | 34 +++++++++++++++++++++----------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/ports/sh/mpconfigport.h b/ports/sh/mpconfigport.h index 539a1330c..4d2058993 100644 --- a/ports/sh/mpconfigport.h +++ b/ports/sh/mpconfigport.h @@ -127,7 +127,3 @@ typedef long mp_off_t; #define MP_STATE_PORT MP_STATE_VM -/* Activate support of unicode string in MicroPython */ -#define MICROPY_PY_BUILTINS_STR_UNICODE (1) - - diff --git a/ports/sh/numworks/modkandinsky.c b/ports/sh/numworks/modkandinsky.c index 443493c69..169745ad9 100644 --- a/ports/sh/numworks/modkandinsky.c +++ b/ports/sh/numworks/modkandinsky.c @@ -228,21 +228,31 @@ static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) { u = 0; v += 16; } else { - /* The following test is for support of unicode characters that are encoded on 2 chars */ - /* This gives text[l]<0 and we need to pass 2 chars to dtext to take care of unicode encoding */ - if(text[l]>=0){ + /* The following test is for support of unicode characters that are encoded on 1 char or more */ + /* we need to pass multiple chars to dtext to take care of unicode encoding */ + if(((unsigned char) text[l]) >= 0x00 && ((unsigned char) text[l]) <= 0x7F){ drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback); - dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, - &text[l], 1); + dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, &text[l], 1); u += 10; - } else { - drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback); - dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, - &text[l], 2); - u += 10; - l++; } - + else if(((unsigned char) text[l]) >= 0x80 && ((unsigned char) text[l]) <= 0xDF){ + drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback); + dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, &text[l], 2); + u += 10; + l+=1; + } + else if(((unsigned char) text[l]) >= 0xE0 && ((unsigned char) text[l]) <= 0xEF){ + drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback); + dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, &text[l], 3); + u += 10; + l+=2; + } + else if(((unsigned char) text[l]) >= 0xF0 && ((unsigned char) text[l]) <= 0xF7){ + drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback); + dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, &text[l], 4); + u += 10; + l+=3; + } } } From d298d0538d68e2fecdc8a7ae9d7641d03ed0d549 Mon Sep 17 00:00:00 2001 From: Sylvain PILLOT Date: Sun, 25 Feb 2024 19:40:53 +0100 Subject: [PATCH 27/27] added the full U+00A characters table for NW module Kandinsky --- ports/fxcg50/PoliceNW/U+0020.png | Bin 1507 -> 22718 bytes ports/fxcg50/PoliceNW/U+00A0.png | Bin 1009 -> 24110 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/ports/fxcg50/PoliceNW/U+0020.png b/ports/fxcg50/PoliceNW/U+0020.png index 80a72d48fb7e31f31c2b9125e64cadd647d60783..247fb3204d47ad03715d916b21a41b8e1194f52e 100644 GIT binary patch literal 22718 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 delta 1480 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�{YE92v>a*K1M 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 R;6n^R;OXk;vd$@?2>^mUkZ%A0