Bläddra i källkod

quick and dirty fap version with wacky polyfill

Oliver Fabel 1 år sedan
förälder
incheckning
10aea55754

+ 0 - 191
.clang-format

@@ -1,191 +0,0 @@
----
-Language:        Cpp
-AccessModifierOffset: -4
-AlignAfterOpenBracket: AlwaysBreak
-AlignArrayOfStructures: None
-AlignConsecutiveMacros: None
-AlignConsecutiveAssignments: None
-AlignConsecutiveBitFields: None
-AlignConsecutiveDeclarations: None
-AlignEscapedNewlines: Left
-AlignOperands:   Align
-AlignTrailingComments: false
-AllowAllArgumentsOnNextLine: true
-AllowAllParametersOfDeclarationOnNextLine: false
-AllowShortEnumsOnASingleLine: true
-AllowShortBlocksOnASingleLine: Never
-AllowShortCaseLabelsOnASingleLine: false
-AllowShortFunctionsOnASingleLine: None
-AllowShortLambdasOnASingleLine: All
-AllowShortIfStatementsOnASingleLine: WithoutElse
-AllowShortLoopsOnASingleLine: true
-AlwaysBreakAfterDefinitionReturnType: None
-AlwaysBreakAfterReturnType: None
-AlwaysBreakBeforeMultilineStrings: false
-AlwaysBreakTemplateDeclarations: Yes
-AttributeMacros:
-  - __capability
-BinPackArguments: false
-BinPackParameters: false
-BraceWrapping:
-  AfterCaseLabel:  false
-  AfterClass:      false
-  AfterControlStatement: Never
-  AfterEnum:       false
-  AfterFunction:   false
-  AfterNamespace:  false
-  AfterObjCDeclaration: false
-  AfterStruct:     false
-  AfterUnion:      false
-  AfterExternBlock: false
-  BeforeCatch:     false
-  BeforeElse:      false
-  BeforeLambdaBody: false
-  BeforeWhile:     false
-  IndentBraces:    false
-  SplitEmptyFunction: true
-  SplitEmptyRecord: true
-  SplitEmptyNamespace: true
-BreakBeforeBinaryOperators: None
-BreakBeforeConceptDeclarations: true
-BreakBeforeBraces: Attach
-BreakBeforeInheritanceComma: false
-BreakInheritanceList: BeforeColon
-BreakBeforeTernaryOperators: false
-BreakConstructorInitializersBeforeComma: false
-BreakConstructorInitializers: BeforeComma
-BreakAfterJavaFieldAnnotations: false
-BreakStringLiterals: false
-ColumnLimit:     120
-CommentPragmas:  '^ IWYU pragma:'
-QualifierAlignment: Leave
-CompactNamespaces: false
-ConstructorInitializerIndentWidth: 4
-ContinuationIndentWidth: 4
-Cpp11BracedListStyle: true
-DeriveLineEnding: true
-DerivePointerAlignment: false
-DisableFormat:   false
-EmptyLineAfterAccessModifier: Never
-EmptyLineBeforeAccessModifier: LogicalBlock
-ExperimentalAutoDetectBinPacking: false
-PackConstructorInitializers: BinPack
-BasedOnStyle:    ''
-ConstructorInitializerAllOnOneLineOrOnePerLine: false
-AllowAllConstructorInitializersOnNextLine: true
-FixNamespaceComments: false
-ForEachMacros:
-  - foreach
-  - Q_FOREACH
-  - BOOST_FOREACH
-IfMacros:
-  - KJ_IF_MAYBE
-IncludeBlocks:   Preserve
-IncludeCategories:
-  - Regex:           '.*'
-    Priority:        1
-    SortPriority:    0
-    CaseSensitive:   false
-  - Regex:           '^(<|"(gtest|gmock|isl|json)/)'
-    Priority:        3
-    SortPriority:    0
-    CaseSensitive:   false
-  - Regex:           '.*'
-    Priority:        1
-    SortPriority:    0
-    CaseSensitive:   false
-IncludeIsMainRegex: '(Test)?$'
-IncludeIsMainSourceRegex: ''
-IndentAccessModifiers: false
-IndentCaseLabels: false
-IndentCaseBlocks: false
-IndentGotoLabels: true
-IndentPPDirectives: None
-IndentExternBlock: AfterExternBlock
-IndentRequires:  false
-IndentWidth:     4
-IndentWrappedFunctionNames: true
-InsertTrailingCommas: None
-JavaScriptQuotes: Leave
-JavaScriptWrapImports: true
-KeepEmptyLinesAtTheStartOfBlocks: false
-LambdaBodyIndentation: Signature
-MacroBlockBegin: ''
-MacroBlockEnd:   ''
-MaxEmptyLinesToKeep: 1
-NamespaceIndentation: None
-ObjCBinPackProtocolList: Auto
-ObjCBlockIndentWidth: 4
-ObjCBreakBeforeNestedBlockParam: true
-ObjCSpaceAfterProperty: true
-ObjCSpaceBeforeProtocolList: true
-PenaltyBreakAssignment: 10
-PenaltyBreakBeforeFirstCallParameter: 30
-PenaltyBreakComment: 10
-PenaltyBreakFirstLessLess: 0
-PenaltyBreakOpenParenthesis: 0
-PenaltyBreakString: 10
-PenaltyBreakTemplateDeclaration: 10
-PenaltyExcessCharacter: 100
-PenaltyReturnTypeOnItsOwnLine: 60
-PenaltyIndentedWhitespace: 0
-PointerAlignment: Left
-PPIndentWidth:   -1
-ReferenceAlignment: Pointer
-ReflowComments:  false
-RemoveBracesLLVM: false
-SeparateDefinitionBlocks: Leave
-ShortNamespaceLines: 1
-SortIncludes:    Never
-SortJavaStaticImport: Before
-SortUsingDeclarations: false
-SpaceAfterCStyleCast: false
-SpaceAfterLogicalNot: false
-SpaceAfterTemplateKeyword: true
-SpaceBeforeAssignmentOperators: true
-SpaceBeforeCaseColon: false
-SpaceBeforeCpp11BracedList: false
-SpaceBeforeCtorInitializerColon: true
-SpaceBeforeInheritanceColon: true
-SpaceBeforeParens: Never
-SpaceBeforeParensOptions:
-  AfterControlStatements: false
-  AfterForeachMacros: false
-  AfterFunctionDefinitionName: false
-  AfterFunctionDeclarationName: false
-  AfterIfMacros:   false
-  AfterOverloadedOperator: false
-  BeforeNonEmptyParentheses: false
-SpaceAroundPointerQualifiers: Default
-SpaceBeforeRangeBasedForLoopColon: true
-SpaceInEmptyBlock: false
-SpaceInEmptyParentheses: false
-SpacesBeforeTrailingComments: 1
-SpacesInAngles:  Never
-SpacesInConditionalStatement: false
-SpacesInContainerLiterals: false
-SpacesInCStyleCastParentheses: false
-SpacesInLineCommentPrefix:
-  Minimum:         1
-  Maximum:         -1
-SpacesInParentheses: false
-SpacesInSquareBrackets: false
-SpaceBeforeSquareBrackets: false
-BitFieldColonSpacing: Both
-Standard:        c++03
-StatementAttributeLikeMacros:
-  - Q_EMIT
-StatementMacros:
-  - Q_UNUSED
-  - QT_REQUIRE_VERSION
-TabWidth:        4
-UseCRLF:         false
-UseTab:          Never
-WhitespaceSensitiveMacros:
-  - STRINGIZE
-  - PP_STRINGIZE
-  - BOOST_PP_STRINGIZE
-  - NS_SWIFT_NAME
-  - CF_SWIFT_NAME
-...
-

+ 0 - 2
.clangd

@@ -1,2 +0,0 @@
-Diagnostics:
-  Suppress: 'expected_fn_body'

+ 14 - 0
.vscode/settings.json

@@ -0,0 +1,14 @@
+{
+  "files.associations": {
+    "py_app.h": "c",
+    "mp_flipper_app.h": "c",
+    "mp_flipper_runtime.h": "c",
+    "mp_flipper_compiler.h": "c",
+    "system_error": "c",
+    "cmath": "c",
+    "bit": "c",
+    "*.tcc": "c",
+    "random": "c",
+    "typeinfo": "c"
+  }
+}

+ 1 - 14
Makefile

@@ -1,18 +1,5 @@
-# Set the location of the top of the MicroPython repository.
-MICROPYTHON_TOP = ./lib/micropython
-
-PACKAGE_DIR = ./lib/micropython-build
-
-SRC_C = ./lib/micropython-port/modflipperzero.c
-
-SRC_QSTR += ./lib/micropython-port/modflipperzero.c
-
-include ./Makefile.micropython
-
-$(PACKAGE_DIR): all
-
 .PHONY: build
 .PHONY: build
-build: $(PACKAGE_DIR)
+build:
 	ufbt build
 	ufbt build
 
 
 .PHONY: launch
 .PHONY: launch

+ 0 - 62
Makefile.micropython

@@ -1,62 +0,0 @@
-# Set the build output directory for the generated files.
-BUILD = build-embed
-
-# Include the core environment definitions; this will set $(TOP).
-include $(MICROPYTHON_TOP)/py/mkenv.mk
-
-# Include py core make definitions.
-include $(TOP)/py/py.mk
-include $(TOP)/extmod/extmod.mk
-
-# Set the location of the MicroPython embed port.
-MICROPYTHON_EMBED_PORT = $(MICROPYTHON_TOP)/ports/embed
-
-# Set default makefile-level MicroPython feature configurations.
-MICROPY_ROM_TEXT_COMPRESSION ?= 0
-
-# Set CFLAGS for the MicroPython build.
-CFLAGS += -I. -I$(TOP) -I$(BUILD) -I$(MICROPYTHON_EMBED_PORT)
-CFLAGS += -Wall -Werror -std=c99
-
-# Define the required generated header files.
-GENHDR_OUTPUT = $(addprefix $(BUILD)/genhdr/, \
-	moduledefs.h \
-	mpversion.h \
-	qstrdefs.generated.h \
-	root_pointers.h \
-	)
-
-# Define the top-level target, the generated output files.
-.PHONY: all
-all: micropython-embed-package
-
-clean: clean-micropython-embed-package
-
-.PHONY: clean-micropython-embed-package
-clean-micropython-embed-package:
-	$(RM) -rf $(PACKAGE_DIR)
-
-PACKAGE_DIR ?= micropython_embed
-PACKAGE_DIR_LIST = $(addprefix $(PACKAGE_DIR)/,py extmod shared/runtime genhdr port)
-
-.PHONY: micropython-embed-package
-micropython-embed-package: $(GENHDR_OUTPUT)
-	$(ECHO) "Generate micropython_embed output:"
-	$(Q)$(RM) -rf $(PACKAGE_DIR_LIST)
-	$(Q)$(MKDIR) -p $(PACKAGE_DIR_LIST)
-	$(ECHO) "- py"
-	$(Q)$(CP) $(TOP)/py/*.[ch] $(PACKAGE_DIR)/py
-	$(ECHO) "- extmod"
-	$(Q)$(CP) $(TOP)/extmod/modplatform.h $(PACKAGE_DIR)/extmod
-	$(Q)$(CP) $(TOP)/extmod/modtime.[ch] $(PACKAGE_DIR)/extmod
-	$(Q)$(CP) $(TOP)/extmod/modrandom.[ch] $(PACKAGE_DIR)/extmod
-	$(ECHO) "- shared"
-	$(Q)$(CP) $(TOP)/shared/runtime/gchelper.h $(PACKAGE_DIR)/shared/runtime
-	$(Q)$(CP) $(TOP)/shared/runtime/gchelper_generic.c $(PACKAGE_DIR)/shared/runtime
-	$(ECHO) "- genhdr"
-	$(Q)$(CP) $(GENHDR_OUTPUT) $(PACKAGE_DIR)/genhdr
-	$(ECHO) "- port"
-	$(Q)$(CP) $(MICROPYTHON_EMBED_PORT)/port/*.[ch] $(PACKAGE_DIR)/port
-
-# Include remaining core make rules.
-include $(TOP)/py/mkrules.mk

+ 0 - 0
Makefile.upylib


+ 1 - 1
application.fam

@@ -19,7 +19,7 @@ App(
     ],
     ],
     fap_private_libs=[
     fap_private_libs=[
         Lib(
         Lib(
-            name="micropython-build",
+            name="micropython",
             cflags=[
             cflags=[
                 "-Wno-error",
                 "-Wno-error",
                 "-w",
                 "-w",

+ 0 - 65
lib/micropython-port/file_reader.c

@@ -1,65 +0,0 @@
-#include <stdio.h>
-
-#include <furi.h>
-#include <storage/storage.h>
-
-#include <py/reader.h>
-
-#include <mp_flipper_app.h>
-
-typedef struct {
-    size_t pointer;
-    char* content;
-    size_t size;
-} FileReaderContext;
-
-FileReaderContext* file_reader_context_alloc(File* file) {
-    FileReaderContext* context = malloc(sizeof(FileReaderContext));
-
-    context->pointer = 0;
-    context->size = storage_file_size(file);
-    context->content = malloc(context->size * sizeof(char));
-
-    storage_file_read(file, context->content, context->size);
-
-    return context;
-}
-
-void file_reader_context_free(FileReaderContext* context) {
-    free(context->content);
-    free(context);
-}
-
-mp_uint_t file_reader_read(void* data) {
-    FileReaderContext* context = data;
-
-    if(context->size == 0 || context->pointer >= context->size) {
-        return MP_READER_EOF;
-    }
-
-    return context->content[context->pointer++];
-}
-
-void file_reader_close(void* data) {
-    file_reader_context_free(data);
-}
-
-void mp_reader_new_file(mp_reader_t* reader, qstr filename) {
-    FuriString* file_path = furi_string_alloc();
-    Storage* storage = furi_record_open(RECORD_STORAGE);
-    File* file = storage_file_alloc(storage);
-
-    furi_string_printf(file_path, "%s/%s", root_module_path, qstr_str(filename));
-
-    if(!storage_file_open(file, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING)) {
-        furi_crash("unable to open file");
-    }
-
-    reader->data = file_reader_context_alloc(file);
-    reader->readbyte = file_reader_read;
-    reader->close = file_reader_close;
-
-    storage_file_free(file);
-    furi_record_close(RECORD_STORAGE);
-    furi_string_free(file_path);
-}

+ 0 - 142
lib/micropython-port/math.c

@@ -1,142 +0,0 @@
-#include <stdint.h>
-#include <float.h>
-#include <math.h>
-
-#define FORCE_EVAL(x)                            \
-    do {                                         \
-        if(sizeof(x) == sizeof(float)) {         \
-            volatile float __x;                  \
-            __x = (x);                           \
-            (void)__x;                           \
-        } else if(sizeof(x) == sizeof(double)) { \
-            volatile double __x;                 \
-            __x = (x);                           \
-            (void)__x;                           \
-        } else {                                 \
-            volatile long double __x;            \
-            __x = (x);                           \
-            (void)__x;                           \
-        }                                        \
-    } while(0)
-
-float floorf(float x) {
-    union {
-        float f;
-        uint32_t i;
-    } u = {x};
-    int e = (int)(u.i >> 23 & 0xff) - 0x7f;
-    uint32_t m;
-
-    if(e >= 23) return x;
-    if(e >= 0) {
-        m = 0x007fffff >> e;
-        if((u.i & m) == 0) return x;
-        FORCE_EVAL(x + 0x1p120f);
-        if(u.i >> 31) u.i += m;
-        u.i &= ~m;
-    } else {
-        FORCE_EVAL(x + 0x1p120f);
-        if(u.i >> 31 == 0)
-            u.i = 0;
-        else if(u.i << 1)
-            u.f = -1.0;
-    }
-    return u.f;
-}
-
-float fmodf(float x, float y) {
-    union {
-        float f;
-        uint32_t i;
-    } ux = {x}, uy = {y};
-    int ex = ux.i >> 23 & 0xff;
-    int ey = uy.i >> 23 & 0xff;
-    uint32_t sx = ux.i & 0x80000000;
-    uint32_t i;
-    uint32_t uxi = ux.i;
-
-    if(uy.i << 1 == 0 || isnan(y) || ex == 0xff) return (x * y) / (x * y);
-    if(uxi << 1 <= uy.i << 1) {
-        if(uxi << 1 == uy.i << 1) return 0 * x;
-        return x;
-    }
-
-    /* normalize x and y */
-    if(!ex) {
-        for(i = uxi << 9; i >> 31 == 0; ex--, i <<= 1)
-            ;
-        uxi <<= -ex + 1;
-    } else {
-        uxi &= -1U >> 9;
-        uxi |= 1U << 23;
-    }
-    if(!ey) {
-        for(i = uy.i << 9; i >> 31 == 0; ey--, i <<= 1)
-            ;
-        uy.i <<= -ey + 1;
-    } else {
-        uy.i &= -1U >> 9;
-        uy.i |= 1U << 23;
-    }
-
-    /* x mod y */
-    for(; ex > ey; ex--) {
-        i = uxi - uy.i;
-        if(i >> 31 == 0) {
-            if(i == 0) return 0 * x;
-            uxi = i;
-        }
-        uxi <<= 1;
-    }
-    i = uxi - uy.i;
-    if(i >> 31 == 0) {
-        if(i == 0) return 0 * x;
-        uxi = i;
-    }
-    for(; uxi >> 23 == 0; uxi <<= 1, ex--)
-        ;
-
-    /* scale result up */
-    if(ex > 0) {
-        uxi -= 1U << 23;
-        uxi |= (uint32_t)ex << 23;
-    } else {
-        uxi >>= -ex + 1;
-    }
-    uxi |= sx;
-    ux.i = uxi;
-    return ux.f;
-}
-
-float nearbyintf(float x) {
-    union {
-        float f;
-        uint32_t i;
-    } u = {x};
-    int e = u.i >> 23 & 0xff;
-    int s = u.i >> 31;
-    float_t y;
-
-    if(e >= 0x7f + 23) return x;
-    if(s)
-        y = x - 0x1p23f + 0x1p23f;
-    else
-        y = x + 0x1p23f - 0x1p23f;
-    if(y == 0) return s ? -0.0f : 0.0f;
-    return y;
-}
-
-//
-// __aebi_x2x functions are just here to prevent compiler warnings
-//
-double __aeabi_i2d(int x) {
-    return (double)x;
-}
-
-float __aeabi_d2f(double x) {
-    return (float)x;
-}
-
-double __aeabi_f2d(float x) {
-    return (double)x;
-}

+ 0 - 124
lib/micropython-port/modflipperzero.c

@@ -1,124 +0,0 @@
-#include <stdio.h>
-
-#include <py/objint.h>
-#include <py/obj.h>
-#include <py/runtime.h>
-
-#include "modflipperzero.h"
-
-static mp_obj_t flipperzero_light_set(mp_obj_t light_obj, mp_obj_t brightness_obj) {
-    mp_int_t light = mp_obj_get_int(light_obj);
-    mp_int_t brightness = mp_obj_get_int(brightness_obj);
-
-    mp_flipper_light_set(light, brightness);
-
-    return mp_const_none;
-}
-static MP_DEFINE_CONST_FUN_OBJ_2(flipperzero_light_set_obj, flipperzero_light_set);
-
-static mp_obj_t flipperzero_light_blink_start(size_t n_args, const mp_obj_t* args) {
-    if(n_args != 4) {
-        return mp_const_none;
-    }
-
-    mp_int_t light = mp_obj_get_int(args[0]);
-    mp_int_t brightness = mp_obj_get_int(args[1]);
-    mp_int_t on_time = mp_obj_get_int(args[2]);
-    mp_int_t period = mp_obj_get_int(args[3]);
-
-    mp_flipper_light_blink_start(light, brightness, on_time, period);
-
-    return mp_const_none;
-}
-static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(flipperzero_light_blink_start_obj, 4, 4, flipperzero_light_blink_start);
-
-static mp_obj_t flipperzero_light_blink_stop() {
-    mp_flipper_light_blink_stop();
-
-    return mp_const_none;
-}
-static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_light_blink_stop_obj, flipperzero_light_blink_stop);
-
-static mp_obj_t flipperzero_light_blink_set_color(mp_obj_t light_obj) {
-    mp_int_t light = mp_obj_get_int(light_obj);
-
-    mp_flipper_light_blink_set_color(light);
-
-    return mp_const_none;
-}
-static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_light_blink_set_color_obj, flipperzero_light_blink_set_color);
-
-static mp_obj_t flipperzero_vibro_set(mp_obj_t state) {
-    bool state_bool = mp_obj_is_true(state);
-
-    mp_flipper_vibro(state_bool);
-
-    return state_bool ? mp_const_true : mp_const_false;
-}
-static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_vibro_set_obj, flipperzero_vibro_set);
-
-static mp_obj_t flipperzero_speaker_start(mp_obj_t frequency_obj, mp_obj_t volume_obj) {
-    mp_float_t frequency = mp_obj_get_float(frequency_obj);
-    mp_float_t volume = mp_obj_get_float(volume_obj);
-
-    return mp_flipper_speaker_start(frequency, volume) ? mp_const_true : mp_const_false;
-}
-static MP_DEFINE_CONST_FUN_OBJ_2(flipperzero_speaker_start_obj, flipperzero_speaker_start);
-
-static mp_obj_t flipperzero_speaker_set_volume(mp_obj_t volume_obj) {
-    mp_float_t volume = mp_obj_get_float(volume_obj);
-
-    return mp_flipper_speaker_set_volume(volume) ? mp_const_true : mp_const_false;
-}
-static MP_DEFINE_CONST_FUN_OBJ_1(flipperzero_speaker_set_volume_obj, flipperzero_speaker_set_volume);
-
-static mp_obj_t flipperzero_speaker_stop() {
-    mp_flipper_speaker_stop();
-
-    return mp_flipper_speaker_stop() ? mp_const_true : mp_const_false;
-}
-static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_speaker_stop_obj, flipperzero_speaker_stop);
-
-static mp_obj_t flipperzero_canvas_draw_dot(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t color_obj) {
-    mp_int_t x = mp_obj_get_int(x_obj);
-    mp_int_t y = mp_obj_get_int(y_obj);
-    bool color = mp_obj_is_true(color_obj);
-
-    mp_flipper_canvas_draw_dot(x, y, color);
-
-    return mp_const_none;
-}
-static MP_DEFINE_CONST_FUN_OBJ_3(flipperzero_canvas_draw_dot_obj, flipperzero_canvas_draw_dot);
-
-static mp_obj_t flipperzero_canvas_update() {
-    mp_flipper_canvas_update();
-
-    return mp_const_none;
-}
-static MP_DEFINE_CONST_FUN_OBJ_0(flipperzero_canvas_update_obj, flipperzero_canvas_update);
-
-static const mp_rom_map_elem_t flipperzero_module_globals_table[] = {
-    {MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_flipperzero)},
-    {MP_ROM_QSTR(MP_QSTR_LIGHT_RED), MP_ROM_INT(MP_FLIPPER_LED_RED)},
-    {MP_ROM_QSTR(MP_QSTR_LIGHT_GREEN), MP_ROM_INT(MP_FLIPPER_LED_GREEN)},
-    {MP_ROM_QSTR(MP_QSTR_LIGHT_BLUE), MP_ROM_INT(MP_FLIPPER_LED_BLUE)},
-    {MP_ROM_QSTR(MP_QSTR_LIGHT_BACKLIGHT), MP_ROM_INT(MP_FLIPPER_LED_BACKLIGHT)},
-    {MP_ROM_QSTR(MP_QSTR_light_set), MP_ROM_PTR(&flipperzero_light_set_obj)},
-    {MP_ROM_QSTR(MP_QSTR_light_blink_start), MP_ROM_PTR(&flipperzero_light_blink_start_obj)},
-    {MP_ROM_QSTR(MP_QSTR_light_blink_set_color), MP_ROM_PTR(&flipperzero_light_blink_set_color_obj)},
-    {MP_ROM_QSTR(MP_QSTR_light_blink_stop), MP_ROM_PTR(&flipperzero_light_blink_stop_obj)},
-    {MP_ROM_QSTR(MP_QSTR_vibro_set), MP_ROM_PTR(&flipperzero_vibro_set_obj)},
-    {MP_ROM_QSTR(MP_QSTR_speaker_start), MP_ROM_PTR(&flipperzero_speaker_start_obj)},
-    {MP_ROM_QSTR(MP_QSTR_speaker_set_volume), MP_ROM_PTR(&flipperzero_speaker_set_volume_obj)},
-    {MP_ROM_QSTR(MP_QSTR_speaker_stop), MP_ROM_PTR(&flipperzero_speaker_stop_obj)},
-    {MP_ROM_QSTR(MP_QSTR_canvas_draw_dot), MP_ROM_PTR(&flipperzero_canvas_draw_dot_obj)},
-    {MP_ROM_QSTR(MP_QSTR_canvas_update), MP_ROM_PTR(&flipperzero_canvas_update_obj)},
-};
-static MP_DEFINE_CONST_DICT(flipperzero_module_globals, flipperzero_module_globals_table);
-
-const mp_obj_module_t flipperzero_module = {
-    .base = {&mp_type_module},
-    .globals = (mp_obj_dict_t*)&flipperzero_module_globals,
-};
-
-MP_REGISTER_MODULE(MP_QSTR_flipperzero, flipperzero_module);

+ 0 - 23
lib/micropython-port/modflipperzero.h

@@ -1,23 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-#include <stdbool.h>
-
-#define MP_FLIPPER_LED_RED (1 << 0)
-#define MP_FLIPPER_LED_GREEN (1 << 1)
-#define MP_FLIPPER_LED_BLUE (1 << 2)
-#define MP_FLIPPER_LED_BACKLIGHT (1 << 3)
-
-void mp_flipper_light_set(uint8_t raw_light, uint8_t brightness);
-void mp_flipper_light_blink_start(uint8_t raw_light, uint8_t brightness, uint16_t on_time, uint16_t period);
-void mp_flipper_light_blink_set_color(uint8_t raw_light);
-void mp_flipper_light_blink_stop();
-
-void mp_flipper_vibro(bool state);
-
-bool mp_flipper_speaker_start(float frequency, float volume);
-bool mp_flipper_speaker_set_volume(float volume);
-bool mp_flipper_speaker_stop();
-
-void mp_flipper_canvas_draw_dot(uint8_t x, uint8_t y, bool color);
-void mp_flipper_canvas_update();

+ 0 - 86
lib/micropython-port/modflipperzero_impl.c

@@ -1,86 +0,0 @@
-#include <stdint.h>
-
-#include <furi_hal.h>
-
-#include <mp_flipper_app.h>
-
-#include "modflipperzero.h"
-
-static Light decode_light(uint8_t value) {
-    Light light = 0;
-
-    light += value & MP_FLIPPER_LED_RED ? LightRed : 0;
-    light += value & MP_FLIPPER_LED_GREEN ? LightGreen : 0;
-    light += value & MP_FLIPPER_LED_BLUE ? LightBlue : 0;
-    light += value & MP_FLIPPER_LED_BACKLIGHT ? LightBacklight : 0;
-
-    return light;
-}
-
-inline void mp_flipper_light_set(uint8_t raw_light, uint8_t brightness) {
-    Light light = decode_light(raw_light);
-
-    furi_hal_light_set(light, brightness);
-}
-
-inline void mp_flipper_light_blink_start(uint8_t raw_light, uint8_t brightness, uint16_t on_time, uint16_t period) {
-    Light light = decode_light(raw_light);
-
-    furi_hal_light_blink_start(light, brightness, on_time, period);
-}
-
-inline void mp_flipper_light_blink_set_color(uint8_t raw_light) {
-    Light light = decode_light(raw_light);
-
-    furi_hal_light_blink_set_color(light);
-}
-
-inline void mp_flipper_light_blink_stop() {
-    furi_hal_light_blink_stop();
-}
-
-inline void mp_flipper_vibro(bool state) {
-    furi_hal_vibro_on(state);
-}
-
-inline bool mp_flipper_speaker_start(float frequency, float volume) {
-    if(furi_hal_speaker_acquire(100)) {
-        furi_hal_speaker_start(frequency, volume);
-
-        return true;
-    }
-
-    return false;
-}
-
-inline bool mp_flipper_speaker_set_volume(float volume) {
-    if(furi_hal_speaker_is_mine()) {
-        furi_hal_speaker_set_volume(volume);
-
-        return true;
-    }
-    return false;
-}
-
-inline bool mp_flipper_speaker_stop() {
-    if(furi_hal_speaker_is_mine()) {
-        furi_hal_speaker_stop();
-
-        furi_hal_speaker_release();
-
-        return true;
-    }
-
-    return false;
-}
-
-inline void mp_flipper_canvas_draw_dot(uint8_t x, uint8_t y, bool color) {
-    size_t index = x / SCREEN_PIXEL_PER_ITEM + y * (SCREEN_WIDTH / SCREEN_PIXEL_PER_ITEM);
-    const uint32_t mask = 1 << (x % SCREEN_PIXEL_PER_ITEM);
-
-    mp_flipper_canvas[index] |= color ? (UINT32_MAX & mask) : 0;
-}
-
-inline void mp_flipper_canvas_update() {
-    view_port_update(mp_flipper_view_port);
-}

+ 0 - 5
lib/micropython-port/modrandom.h

@@ -1,5 +0,0 @@
-#pragma once
-
-#include <stdint.h>
-
-uint32_t mp_flipper_seed_init();

+ 0 - 32
lib/micropython-port/modtime.c

@@ -1,32 +0,0 @@
-#include <furi.h>
-#include <furi_hal.h>
-
-#include <py/mphal.h>
-
-mp_uint_t mp_time_time_get(void) {
-    return furi_hal_rtc_get_timestamp();
-}
-
-uint64_t mp_hal_time_ns(void) {
-    return furi_hal_rtc_get_timestamp() * 1000;
-}
-
-mp_uint_t mp_hal_ticks_ms(void) {
-    return furi_kernel_get_tick_frequency() / 1000;
-}
-
-mp_uint_t mp_hal_ticks_us(void) {
-    return furi_kernel_get_tick_frequency() / 1000000;
-}
-
-mp_uint_t mp_hal_ticks_cpu(void) {
-    return furi_get_tick();
-}
-
-void mp_hal_delay_ms(mp_uint_t ms) {
-    furi_delay_ms(ms);
-}
-
-void mp_hal_delay_us(mp_uint_t us) {
-    furi_delay_us(us);
-}

+ 10 - 0
lib/micropython-port/mp_flipper_context.h

@@ -0,0 +1,10 @@
+#include <furi.h>
+#include <gui/gui.h>
+
+typedef struct {
+    Gui* gui;
+    ViewPort* view_port;
+    Canvas* canvas;
+    FuriPubSub* input_event_queue;
+    FuriPubSubSubscription* input_event;
+} mp_flipper_context_t;

+ 56 - 0
lib/micropython-port/mp_flipper_file_helper.c

@@ -0,0 +1,56 @@
+#include <mp_flipper_runtime.h>
+#include <mp_flipper_halport.h>
+
+#include <furi.h>
+#include <storage/storage.h>
+
+mp_flipper_import_stat_t mp_flipper_try_resolve_filesystem_path(FuriString* path) {
+    const char* path_str = furi_string_get_cstr(path);
+    FuriString* _path = furi_string_alloc_printf("%s", path_str);
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+
+    mp_flipper_import_stat_t stat = MP_FLIPPER_IMPORT_STAT_FILE;
+
+    FS_Error error;
+    FileInfo info;
+
+    do {
+        // make path absolute
+        if(!furi_string_start_with_str(_path, "/")) {
+            furi_string_printf(_path, "%s/%s", mp_flipper_root_module_path, path_str);
+        }
+
+        // check if file or folder exists
+        error = storage_common_stat(storage, furi_string_get_cstr(_path), &info);
+        if(error == FSE_OK) {
+            break;
+        }
+
+        // check for existing python file
+        furi_string_cat_str(_path, ".py");
+
+        error = storage_common_stat(storage, furi_string_get_cstr(_path), &info);
+        if(error == FSE_OK) {
+            break;
+        }
+    } while(false);
+
+    // file or folder missing
+    if(error == FSE_NOT_EXIST) {
+        stat = MP_FLIPPER_IMPORT_STAT_NO_EXIST;
+    }
+    // abort on error
+    else if(error != FSE_OK) {
+        mp_flipper_raise_os_error_with_filename(MP_ENOENT, furi_string_get_cstr(path));
+    }
+    // path points to directory
+    else if((info.flags & FSF_DIRECTORY) == FSF_DIRECTORY) {
+        stat = MP_FLIPPER_IMPORT_STAT_DIR;
+    }
+
+    furi_record_close(RECORD_STORAGE);
+
+    furi_string_move(path, _path);
+
+    return stat;
+}

+ 7 - 0
lib/micropython-port/mp_flipper_file_helper.h

@@ -0,0 +1,7 @@
+#pragma once
+
+#include <mp_flipper_halport.h>
+
+#include <furi.h>
+
+mp_flipper_import_stat_t mp_flipper_try_resolve_filesystem_path(FuriString* path);

+ 76 - 0
lib/micropython-port/mp_flipper_file_reader.c

@@ -0,0 +1,76 @@
+#include <stdio.h>
+
+#include <furi.h>
+#include <storage/storage.h>
+
+#include <mp_flipper_runtime.h>
+#include <mp_flipper_file_reader.h>
+
+#include "mp_flipper_file_helper.h"
+
+typedef struct {
+    size_t pointer;
+    FuriString* content;
+    size_t size;
+} FileReaderContext;
+
+inline void* mp_flipper_file_reader_context_alloc(const char* filename) {
+    FuriString* path = furi_string_alloc_printf("%s", filename);
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    File* file = storage_file_alloc(storage);
+    FileReaderContext* ctx = NULL;
+
+    do {
+        if(mp_flipper_try_resolve_filesystem_path(path) == MP_FLIPPER_IMPORT_STAT_NO_EXIST) {
+            furi_string_free(path);
+
+            mp_flipper_raise_os_error_with_filename(MP_ENOENT, filename);
+        }
+
+        if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) {
+            storage_file_free(file);
+
+            mp_flipper_raise_os_error_with_filename(MP_ENOENT, filename);
+
+            break;
+        }
+
+        ctx = malloc(sizeof(FileReaderContext));
+
+        ctx->pointer = 0;
+        ctx->content = furi_string_alloc();
+        ctx->size = storage_file_size(file);
+
+        char character = '\0';
+
+        for(size_t i = 0; i < ctx->size; i++) {
+            storage_file_read(file, &character, 1);
+
+            furi_string_push_back(ctx->content, character);
+        }
+    } while(false);
+
+    storage_file_free(file);
+    furi_record_close(RECORD_STORAGE);
+    furi_string_free(path);
+
+    return ctx;
+}
+
+inline uint32_t mp_flipper_file_reader_read(void* data) {
+    FileReaderContext* ctx = data;
+
+    if(ctx->pointer >= ctx->size) {
+        return MP_FLIPPER_FILE_READER_EOF;
+    }
+
+    return furi_string_get_char(ctx->content, ctx->pointer++);
+}
+
+void mp_flipper_file_reader_close(void* data) {
+    FileReaderContext* ctx = data;
+
+    furi_string_free(ctx->content);
+
+    free(data);
+}

+ 29 - 0
lib/micropython-port/mp_flipper_halport.c

@@ -0,0 +1,29 @@
+#include <stdio.h>
+
+#include <furi.h>
+#include <storage/storage.h>
+
+#include <mp_flipper_runtime.h>
+#include <mp_flipper_halport.h>
+
+#include "mp_flipper_file_helper.h"
+
+inline void mp_flipper_stdout_tx_str(const char* str) {
+    printf("%s", str);
+}
+
+inline void mp_flipper_stdout_tx_strn_cooked(const char* str, size_t len) {
+    printf("%.*s", len, str);
+}
+
+inline mp_flipper_import_stat_t mp_flipper_import_stat(const char* path) {
+    FuriString* file_path = furi_string_alloc_printf("%s", path);
+
+    mp_flipper_import_stat_t stat = mp_flipper_try_resolve_filesystem_path(file_path);
+
+    stat = furi_string_end_with_str(file_path, path) ? stat : MP_FLIPPER_IMPORT_STAT_NO_EXIST;
+
+    furi_string_free(file_path);
+
+    return stat;
+}

+ 108 - 0
lib/micropython-port/mp_flipper_modflipperzero_canvas.c

@@ -0,0 +1,108 @@
+#include <gui/gui.h>
+
+#include <mp_flipper_modflipperzero.h>
+#include <mp_flipper_runtime.h>
+
+#include "mp_flipper_context.h"
+
+static Align text_align_x = AlignLeft;
+static Align text_align_y = AlignTop;
+
+inline uint8_t mp_flipper_canvas_width() {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    return canvas_width(ctx->canvas);
+}
+
+inline uint8_t mp_flipper_canvas_height() {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    return canvas_height(ctx->canvas);
+}
+
+inline uint8_t mp_flipper_canvas_text_width(const char* text) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    return canvas_string_width(ctx->canvas, text);
+}
+
+inline uint8_t mp_flipper_canvas_text_height() {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    return canvas_current_font_height(ctx->canvas);
+}
+
+inline void mp_flipper_canvas_draw_dot(uint8_t x, uint8_t y) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_dot(ctx->canvas, x, y);
+}
+
+inline void mp_flipper_canvas_draw_box(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t r) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_rbox(ctx->canvas, x, y, w, h, r);
+}
+
+inline void mp_flipper_canvas_draw_frame(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t r) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_rframe(ctx->canvas, x, y, w, h, r);
+}
+
+inline void mp_flipper_canvas_draw_line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_line(ctx->canvas, x0, y0, x1, y1);
+}
+
+inline void mp_flipper_canvas_draw_circle(uint8_t x, uint8_t y, uint8_t r) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_circle(ctx->canvas, x, y, r);
+}
+
+inline void mp_flipper_canvas_draw_disc(uint8_t x, uint8_t y, uint8_t r) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_disc(ctx->canvas, x, y, r);
+}
+
+inline void mp_flipper_canvas_set_font(uint8_t font) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_set_font(
+        ctx->canvas, font == MP_FLIPPER_CANVAS_FONT_PRIMARY ? FontPrimary : FontSecondary);
+}
+
+inline void mp_flipper_canvas_set_color(uint8_t color) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_set_color(ctx->canvas, color == MP_FLIPPER_COLOR_BLACK ? ColorBlack : ColorWhite);
+}
+
+inline void mp_flipper_canvas_set_text(uint8_t x, uint8_t y, const char* text) {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_draw_str_aligned(ctx->canvas, x, y, text_align_x, text_align_y, text);
+}
+
+inline void mp_flipper_canvas_set_text_align(uint8_t x, uint8_t y) {
+    Align align_x = x == MP_FLIPPER_CANVAS_ALIGN_BEGIN ? AlignLeft : AlignRight;
+    Align align_y = y == MP_FLIPPER_CANVAS_ALIGN_BEGIN ? AlignTop : AlignBottom;
+
+    text_align_x = x == MP_FLIPPER_CANVAS_ALIGN_CENTER ? AlignCenter : align_x;
+    text_align_y = y == MP_FLIPPER_CANVAS_ALIGN_CENTER ? AlignCenter : align_y;
+}
+
+inline void mp_flipper_canvas_update() {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_commit(ctx->canvas);
+}
+
+inline void mp_flipper_canvas_clear() {
+    mp_flipper_context_t* ctx = mp_flipper_context;
+
+    canvas_clear(ctx->canvas);
+}

+ 40 - 0
lib/micropython-port/mp_flipper_modflipperzero_light.c

@@ -0,0 +1,40 @@
+#include <furi_hal.h>
+
+#include <mp_flipper_modflipperzero.h>
+
+static Light decode_light(uint8_t value) {
+    Light light = 0;
+
+    light += value & MP_FLIPPER_LED_RED ? LightRed : 0;
+    light += value & MP_FLIPPER_LED_GREEN ? LightGreen : 0;
+    light += value & MP_FLIPPER_LED_BLUE ? LightBlue : 0;
+    light += value & MP_FLIPPER_LED_BACKLIGHT ? LightBacklight : 0;
+
+    return light;
+}
+
+inline void mp_flipper_light_set(uint8_t raw_light, uint8_t brightness) {
+    Light light = decode_light(raw_light);
+
+    furi_hal_light_set(light, brightness);
+}
+
+inline void mp_flipper_light_blink_start(
+    uint8_t raw_light,
+    uint8_t brightness,
+    uint16_t on_time,
+    uint16_t period) {
+    Light light = decode_light(raw_light);
+
+    furi_hal_light_blink_start(light, brightness, on_time, period);
+}
+
+inline void mp_flipper_light_blink_set_color(uint8_t raw_light) {
+    Light light = decode_light(raw_light);
+
+    furi_hal_light_blink_set_color(light);
+}
+
+inline void mp_flipper_light_blink_stop() {
+    furi_hal_light_blink_stop();
+}

+ 35 - 0
lib/micropython-port/mp_flipper_modflipperzero_speaker.c

@@ -0,0 +1,35 @@
+#include <furi_hal.h>
+
+#include <mp_flipper_modflipperzero.h>
+
+inline bool mp_flipper_speaker_start(float frequency, float volume) {
+    if(furi_hal_speaker_acquire(100)) {
+        furi_hal_speaker_start(frequency, volume);
+
+        return true;
+    }
+
+    return false;
+}
+
+inline bool mp_flipper_speaker_set_volume(float volume) {
+    if(furi_hal_speaker_is_mine()) {
+        furi_hal_speaker_set_volume(volume);
+
+        return true;
+    }
+
+    return false;
+}
+
+inline bool mp_flipper_speaker_stop() {
+    if(furi_hal_speaker_is_mine()) {
+        furi_hal_speaker_stop();
+
+        furi_hal_speaker_release();
+
+        return true;
+    }
+
+    return false;
+}

+ 7 - 0
lib/micropython-port/mp_flipper_modflipperzero_vibro.c

@@ -0,0 +1,7 @@
+#include <furi_hal.h>
+
+#include <mp_flipper_modflipperzero.h>
+
+inline void mp_flipper_vibro(bool state) {
+    furi_hal_vibro_on(state);
+}

+ 2 - 2
lib/micropython-port/modrandom.c → lib/micropython-port/mp_flipper_modrandom.c

@@ -1,8 +1,8 @@
 #include <furi_hal.h>
 #include <furi_hal.h>
 
 
-#include "modrandom.h"
+#include <mp_flipper_modrandom.h>
 
 
-uint32_t mp_flipper_seed_init() {
+inline uint32_t mp_flipper_seed_init() {
     furi_hal_random_init();
     furi_hal_random_init();
 
 
     return furi_hal_random_get();
     return furi_hal_random_get();

+ 24 - 0
lib/micropython-port/mp_flipper_modtime.c

@@ -0,0 +1,24 @@
+#include <furi.h>
+#include <furi_hal.h>
+
+#include <mp_flipper_modtime.h>
+
+inline uint32_t mp_flipper_get_timestamp() {
+    return furi_hal_rtc_get_timestamp();
+}
+
+inline uint32_t mp_flipper_get_tick_frequency() {
+    return furi_kernel_get_tick_frequency();
+}
+
+inline uint32_t mp_flipper_get_tick() {
+    return furi_get_tick();
+}
+
+inline void mp_flipper_delay_ms(uint32_t ms) {
+    furi_delay_ms(ms);
+}
+
+inline void mp_flipper_delay_us(uint32_t us) {
+    furi_delay_us(us);
+}

+ 100 - 0
lib/micropython-port/mp_flipper_runtime.c

@@ -0,0 +1,100 @@
+#include <furi.h>
+#include <storage/storage.h>
+
+#include <mp_flipper_runtime.h>
+#include <mp_flipper_modflipperzero.h>
+
+#include "mp_flipper_context.h"
+
+static void on_input_callback(InputEvent* event, void* ctx) {
+    uint16_t button = 1 << event->key;
+    uint16_t type = 1 << (InputKeyMAX + event->type);
+
+    mp_flipper_on_input(button, type);
+}
+
+void mp_flipper_save_file(const char* file_path, const char* data, size_t size) {
+    Storage* storage = furi_record_open(RECORD_STORAGE);
+    File* file = storage_file_alloc(storage);
+
+    do {
+        if(!storage_file_open(file, file_path, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
+            storage_file_free(file);
+
+            mp_flipper_raise_os_error_with_filename(MP_ENOENT, file_path);
+
+            break;
+        }
+
+        storage_file_write(file, data, size);
+    } while(false);
+
+    storage_file_free(file);
+    furi_record_close(RECORD_STORAGE);
+}
+
+inline void mp_flipper_nlr_jump_fail(void* val) {
+    furi_crash();
+}
+
+inline void mp_flipper_assert(const char* file, int line, const char* func, const char* expr) {
+}
+
+inline void mp_flipper_fatal_error(const char* msg) {
+    furi_crash(msg);
+}
+
+const char* mp_flipper_print_get_data(void* data) {
+    return furi_string_get_cstr(data);
+}
+
+size_t mp_flipper_print_get_data_length(void* data) {
+    return furi_string_size(data);
+}
+
+void* mp_flipper_print_data_alloc() {
+    return furi_string_alloc();
+}
+
+void mp_flipper_print_strn(void* data, const char* str, size_t length) {
+    for(size_t i = 0; i < length; i++) {
+        furi_string_push_back(data, str[i]);
+    }
+}
+
+void mp_flipper_print_data_free(void* data) {
+    furi_string_free(data);
+}
+
+void* mp_flipper_context_alloc() {
+    mp_flipper_context_t* ctx = malloc(sizeof(mp_flipper_context_t));
+
+    ctx->gui = furi_record_open(RECORD_GUI);
+    ctx->view_port = view_port_alloc();
+
+    ctx->input_event_queue = furi_record_open(RECORD_INPUT_EVENTS);
+    ctx->input_event = furi_pubsub_subscribe(ctx->input_event_queue, on_input_callback, NULL);
+
+    gui_add_view_port(ctx->gui, ctx->view_port, GuiLayerFullscreen);
+
+    ctx->canvas = gui_direct_draw_acquire(ctx->gui);
+
+    return ctx;
+}
+
+void mp_flipper_context_free(void* context) {
+    mp_flipper_context_t* ctx = context;
+
+    gui_direct_draw_release(ctx->gui);
+
+    furi_pubsub_unsubscribe(ctx->input_event_queue, ctx->input_event);
+
+    gui_remove_view_port(ctx->gui, ctx->view_port);
+
+    view_port_free(ctx->view_port);
+
+    furi_record_close(RECORD_GUI);
+    furi_record_close(RECORD_INPUT_EVENTS);
+
+    free(ctx);
+}

+ 0 - 34
lib/micropython-port/mphalport.c

@@ -1,34 +0,0 @@
-#include <stdio.h>
-
-#include <furi.h>
-#include <storage/storage.h>
-
-#include <py/mphal.h>
-#include <py/builtin.h>
-
-#include <mp_flipper_app.h>
-
-const mp_obj_fun_builtin_var_t mp_builtin_open_obj;
-
-void mp_hal_stdout_tx_str(const char* str) {
-    printf("%s", str);
-}
-
-mp_import_stat_t mp_import_stat(const char* path) {
-    Storage* storage = furi_record_open(RECORD_STORAGE);
-    FuriString* file_path = furi_string_alloc();
-    mp_import_stat_t stat = MP_IMPORT_STAT_NO_EXIST;
-
-    furi_string_printf(file_path, "%s/%s", root_module_path, path);
-
-    FileInfo* info = NULL;
-
-    if(storage_common_stat(storage, furi_string_get_cstr(file_path), info) == FSE_OK) {
-        stat = (info && info->flags & FSF_DIRECTORY) == FSF_DIRECTORY ? MP_IMPORT_STAT_DIR : MP_IMPORT_STAT_FILE;
-    }
-
-    furi_string_free(file_path);
-    furi_record_close(RECORD_STORAGE);
-
-    return stat;
-}

+ 0 - 2
lib/micropython-port/mphalport.h

@@ -1,2 +0,0 @@
-// Define so there's no dependency on extmod/virtpin.h
-#define mp_hal_pin_obj_t

+ 441 - 0
lib/micropython-port/polyfill.c

@@ -0,0 +1,441 @@
+#include <limits.h>
+#include <stdint.h>
+#include <float.h>
+#include <math.h>
+#include <string.h>
+
+#define POLYFILL_FUN_1(name, ret, arg1) \
+  ret name(arg1) {}
+#define POLYFILL_FUN_2(name, ret, arg1, arg2) \
+  ret name(arg1, arg2) {}
+#define POLYFILL_FUN_3(name, ret, arg1, arg2, arg3) \
+  ret name(arg1, arg2, arg3) {}
+#define POLYFILL_FUN_4(name, ret, arg1, arg2, arg3, arg4) \
+  ret name(arg1, arg2, arg3, arg4) {}
+
+#ifndef __aeabi_dcmple
+POLYFILL_FUN_2(__aeabi_dcmple, double, double, double)
+#endif
+
+#ifndef __aeabi_dcmplt
+POLYFILL_FUN_2(__aeabi_dcmplt, double, double, double)
+#endif
+
+#ifndef __aeabi_dcmpun
+POLYFILL_FUN_2(__aeabi_dcmpun, double, double, double)
+#endif
+
+#ifndef __aeabi_dcmpeq
+POLYFILL_FUN_2(__aeabi_dcmpeq, double, double, double)
+#endif
+
+#ifndef __aeabi_dmul
+POLYFILL_FUN_2(__aeabi_dmul, double, double, double)
+#endif
+
+#ifndef __aeabi_dadd
+POLYFILL_FUN_2(__aeabi_dadd, double, double, double)
+#endif
+
+#ifndef __aeabi_ddiv
+POLYFILL_FUN_2(__aeabi_ddiv, double, double, double)
+#endif
+
+#ifndef __aeabi_l2d
+POLYFILL_FUN_1(__aeabi_l2d, double, long)
+#endif
+
+#ifndef __aeabi_f2d
+POLYFILL_FUN_1(__aeabi_f2d, double, float)
+#endif
+
+#ifndef __aeabi_dsub
+POLYFILL_FUN_2(__aeabi_dsub, double, double, double)
+#endif
+
+#ifndef __aeabi_dcmpge
+POLYFILL_FUN_2(__aeabi_dcmpge, double, double, double)
+#endif
+
+#ifndef __aeabi_i2d
+POLYFILL_FUN_1(__aeabi_i2d, double, long)
+#endif
+
+#ifndef __aeabi_dcmpgt
+POLYFILL_FUN_1(__aeabi_dcmpgt, double, double)
+#endif
+
+#ifndef __aeabi_d2iz
+POLYFILL_FUN_1(__aeabi_d2iz, long int, double)
+#endif
+
+#ifndef __aeabi_d2lz
+POLYFILL_FUN_1(__aeabi_d2lz, long, double)
+#endif
+
+#ifndef __aeabi_d2uiz
+POLYFILL_FUN_1(__aeabi_d2uiz, unsigned long int, double)
+#endif
+
+#ifndef __aeabi_d2f
+POLYFILL_FUN_1(__aeabi_d2f, float, double)
+#endif
+
+#ifndef __aeabi_ldivmod
+POLYFILL_FUN_2(__aeabi_ldivmod, long, long, long)
+#endif
+
+#ifndef strtox
+POLYFILL_FUN_4(strtox, long long unsigned int, const char *, char **, int, long long unsigned int)
+#endif
+
+#if FLT_EVAL_METHOD == 0 || FLT_EVAL_METHOD == 1
+#define EPS DBL_EPSILON
+#elif FLT_EVAL_METHOD == 2
+#define EPS LDBL_EPSILON
+#endif
+static const double_t toint = 1 / EPS;
+
+#ifndef FORCE_EVAL
+#define FORCE_EVAL(x)                     \
+  do                                      \
+  {                                       \
+    if (sizeof(x) == sizeof(float))       \
+    {                                     \
+      volatile float __x;                 \
+      __x = (x);                          \
+      (void)__x;                          \
+    }                                     \
+    else if (sizeof(x) == sizeof(double)) \
+    {                                     \
+      volatile double __x;                \
+      __x = (x);                          \
+      (void)__x;                          \
+    }                                     \
+    else                                  \
+    {                                     \
+      volatile long double __x;           \
+      __x = (x);                          \
+      (void)__x;                          \
+    }                                     \
+  } while (0)
+#endif
+
+#ifndef floorf
+float floorf(float x)
+{
+  union
+  {
+    float f;
+    uint32_t i;
+  } u = {x};
+  int e = (int)(u.i >> 23 & 0xff) - 0x7f;
+  uint32_t m;
+
+  if (e >= 23)
+    return x;
+  if (e >= 0)
+  {
+    m = 0x007fffff >> e;
+    if ((u.i & m) == 0)
+      return x;
+    FORCE_EVAL(x + 0x1p120f);
+    if (u.i >> 31)
+      u.i += m;
+    u.i &= ~m;
+  }
+  else
+  {
+    FORCE_EVAL(x + 0x1p120f);
+    if (u.i >> 31 == 0)
+      u.i = 0;
+    else if (u.i << 1)
+      u.f = -1.0;
+  }
+  return u.f;
+}
+#endif
+
+#ifndef floor
+double floor(double x)
+{
+  union
+  {
+    double f;
+    uint64_t i;
+  } u = {x};
+  int e = u.i >> 52 & 0x7ff;
+  double_t y;
+
+  if (e >= 0x3ff + 52 || x == 0)
+    return x;
+  /* y = int(x) - x, where int(x) is an integer neighbor of x */
+  if (u.i >> 63)
+    y = x - toint + toint - x;
+  else
+    y = x + toint - toint - x;
+  /* special case because of non-nearest rounding modes */
+  if (e <= 0x3ff - 1)
+  {
+    FORCE_EVAL(y);
+    return u.i >> 63 ? -1 : 0;
+  }
+  if (y > 0)
+    return x + y - 1;
+  return x + y;
+}
+#endif
+
+#ifndef fmodf
+float fmodf(float x, float y)
+{
+  union
+  {
+    float f;
+    uint32_t i;
+  } ux = {x}, uy = {y};
+  int ex = ux.i >> 23 & 0xff;
+  int ey = uy.i >> 23 & 0xff;
+  uint32_t sx = ux.i & 0x80000000;
+  uint32_t i;
+  uint32_t uxi = ux.i;
+
+  if (uy.i << 1 == 0 || isnan(y) || ex == 0xff)
+    return (x * y) / (x * y);
+  if (uxi << 1 <= uy.i << 1)
+  {
+    if (uxi << 1 == uy.i << 1)
+      return 0 * x;
+    return x;
+  }
+
+  /* normalize x and y */
+  if (!ex)
+  {
+    for (i = uxi << 9; i >> 31 == 0; ex--, i <<= 1)
+      ;
+    uxi <<= -ex + 1;
+  }
+  else
+  {
+    uxi &= -1U >> 9;
+    uxi |= 1U << 23;
+  }
+  if (!ey)
+  {
+    for (i = uy.i << 9; i >> 31 == 0; ey--, i <<= 1)
+      ;
+    uy.i <<= -ey + 1;
+  }
+  else
+  {
+    uy.i &= -1U >> 9;
+    uy.i |= 1U << 23;
+  }
+
+  /* x mod y */
+  for (; ex > ey; ex--)
+  {
+    i = uxi - uy.i;
+    if (i >> 31 == 0)
+    {
+      if (i == 0)
+        return 0 * x;
+      uxi = i;
+    }
+    uxi <<= 1;
+  }
+  i = uxi - uy.i;
+  if (i >> 31 == 0)
+  {
+    if (i == 0)
+      return 0 * x;
+    uxi = i;
+  }
+  for (; uxi >> 23 == 0; uxi <<= 1, ex--)
+    ;
+
+  /* scale result up */
+  if (ex > 0)
+  {
+    uxi -= 1U << 23;
+    uxi |= (uint32_t)ex << 23;
+  }
+  else
+  {
+    uxi >>= -ex + 1;
+  }
+  uxi |= sx;
+  ux.i = uxi;
+  return ux.f;
+}
+#endif
+
+#ifndef fmod
+double fmod(double x, double y)
+{
+  union
+  {
+    double f;
+    uint64_t i;
+  } ux = {x}, uy = {y};
+  int ex = ux.i >> 52 & 0x7ff;
+  int ey = uy.i >> 52 & 0x7ff;
+  int sx = ux.i >> 63;
+  uint64_t i;
+
+  /* in the followings uxi should be ux.i, but then gcc wrongly adds */
+  /* float load/store to inner loops ruining performance and code size */
+  uint64_t uxi = ux.i;
+
+  if (uy.i << 1 == 0 || isnan(y) || ex == 0x7ff)
+    return (x * y) / (x * y);
+  if (uxi << 1 <= uy.i << 1)
+  {
+    if (uxi << 1 == uy.i << 1)
+      return 0 * x;
+    return x;
+  }
+
+  /* normalize x and y */
+  if (!ex)
+  {
+    for (i = uxi << 12; i >> 63 == 0; ex--, i <<= 1)
+      ;
+    uxi <<= -ex + 1;
+  }
+  else
+  {
+    uxi &= -1ULL >> 12;
+    uxi |= 1ULL << 52;
+  }
+  if (!ey)
+  {
+    for (i = uy.i << 12; i >> 63 == 0; ey--, i <<= 1)
+      ;
+    uy.i <<= -ey + 1;
+  }
+  else
+  {
+    uy.i &= -1ULL >> 12;
+    uy.i |= 1ULL << 52;
+  }
+
+  /* x mod y */
+  for (; ex > ey; ex--)
+  {
+    i = uxi - uy.i;
+    if (i >> 63 == 0)
+    {
+      if (i == 0)
+        return 0 * x;
+      uxi = i;
+    }
+    uxi <<= 1;
+  }
+  i = uxi - uy.i;
+  if (i >> 63 == 0)
+  {
+    if (i == 0)
+      return 0 * x;
+    uxi = i;
+  }
+  for (; uxi >> 52 == 0; uxi <<= 1, ex--)
+    ;
+
+  /* scale result */
+  if (ex > 0)
+  {
+    uxi -= 1ULL << 52;
+    uxi |= (uint64_t)ex << 52;
+  }
+  else
+  {
+    uxi >>= -ex + 1;
+  }
+  uxi |= (uint64_t)sx << 63;
+  ux.i = uxi;
+  return ux.f;
+}
+#endif
+
+#ifndef nearbyintf
+float nearbyintf(float x)
+{
+  union
+  {
+    float f;
+    uint32_t i;
+  } u = {x};
+  int e = u.i >> 23 & 0xff;
+  int s = u.i >> 31;
+  float_t y;
+
+  if (e >= 0x7f + 23)
+    return x;
+  if (s)
+    y = x - 0x1p23f + 0x1p23f;
+  else
+    y = x + 0x1p23f - 0x1p23f;
+  if (y == 0)
+    return s ? -0.0f : 0.0f;
+  return y;
+}
+#endif
+
+#ifndef stroll
+long long strtoll(const char *restrict s, char **restrict p, int base)
+{
+  return strtox(s, p, base, LLONG_MIN);
+}
+#endif
+
+#ifndef pow
+double pow(double x, double y)
+{
+  return powf(x, y);
+}
+#endif
+
+#ifndef rint
+
+double rint(double x)
+{
+  union
+  {
+    double f;
+    uint64_t i;
+  } u = {x};
+  int e = u.i >> 52 & 0x7ff;
+  int s = u.i >> 63;
+  double_t y;
+
+  if (e >= 0x3ff + 52)
+    return x;
+  if (s)
+    y = x - toint + toint;
+  else
+    y = x + toint - toint;
+  if (y == 0)
+    return s ? -0.0 : 0;
+  return y;
+}
+#endif
+
+#ifndef nearbyint
+double nearbyint(double x)
+{
+#ifdef FE_INEXACT
+#pragma STDC FENV_ACCESS ON
+  int e;
+
+  e = fetestexcept(FE_INEXACT);
+#endif
+  x = rint(x);
+#ifdef FE_INEXACT
+  if (!e)
+    feclearexcept(FE_INEXACT);
+#endif
+  return x;
+}
+#endif

+ 27 - 78
mp_flipper_app.c

@@ -4,44 +4,22 @@
 #include <dialogs/dialogs.h>
 #include <dialogs/dialogs.h>
 #include <storage/storage.h>
 #include <storage/storage.h>
 
 
-#include <port/micropython_embed.h>
+#include <mp_flipper_runtime.h>
 
 
 #include "mp_flipper_app.h"
 #include "mp_flipper_app.h"
+#include "py_app.h"
 
 
-const char* root_module_path;
+const char *root_module_path;
 
 
-uint32_t* mp_flipper_canvas;
-ViewPort* mp_flipper_view_port;
-FuriMessageQueue* mp_flipper_event_queue;
+ViewPort *mp_flipper_view_port;
+FuriMessageQueue *mp_flipper_event_queue;
 
 
-static void draw_callback(Canvas* canvas, void* ctx) {
-    UNUSED(ctx);
+static void load_python_file(FuriString *file_path, FuriString *code)
+{
+    Storage *storage = furi_record_open(RECORD_STORAGE);
+    DialogsApp *dialogs = furi_record_open(RECORD_DIALOGS);
 
 
-    uint8_t x = 0, y = 0;
-    size_t i = 0;
-    uint32_t mask = 1;
-    Color color = ColorBlack;
-
-    for(size_t index = 0; index < (SCREEN_WIDTH * SCREEN_HEIGHT) / SCREEN_PIXEL_PER_ITEM; index++) {
-        for(i = 0, mask = 1; i < SCREEN_PIXEL_PER_ITEM; i++, mask <<= 1) {
-            color = (mp_flipper_canvas[index] & mask) > 0 ? ColorBlack : ColorWhite;
-
-            canvas_set_color(canvas, color);
-            canvas_draw_dot(canvas, x, y);
-
-            if(++x % SCREEN_WIDTH == 0) {
-                x = 0;
-                y++;
-            }
-        }
-    }
-}
-
-static void load_python_file(FuriString* file_path, FuriString* code) {
-    Storage* storage = furi_record_open(RECORD_STORAGE);
-    DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
-
-    File* file = storage_file_alloc(storage);
+    File *file = storage_file_alloc(storage);
     DialogsFileBrowserOptions browser_options;
     DialogsFileBrowserOptions browser_options;
 
 
     dialog_file_browser_set_basic_options(&browser_options, "py", NULL);
     dialog_file_browser_set_basic_options(&browser_options, "py", NULL);
@@ -51,24 +29,30 @@ static void load_python_file(FuriString* file_path, FuriString* code) {
 
 
     bool result = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
     bool result = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
 
 
-    if(result) {
+    if (result)
+    {
         result = storage_file_open(file, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING);
         result = storage_file_open(file, furi_string_get_cstr(file_path), FSAM_READ, FSOM_OPEN_EXISTING);
     }
     }
 
 
-    if(result) {
+    if (result)
+    {
         size_t read = 0;
         size_t read = 0;
-        do {
+        do
+        {
             uint8_t buffer[64] = {'\0'};
             uint8_t buffer[64] = {'\0'};
 
 
             read = storage_file_read(file, buffer, sizeof(buffer) - 1);
             read = storage_file_read(file, buffer, sizeof(buffer) - 1);
 
 
-            for(size_t i = 0; i < read; i++) {
+            for (size_t i = 0; i < read; i++)
+            {
                 furi_string_push_back(code, buffer[i]);
                 furi_string_push_back(code, buffer[i]);
             }
             }
-        } while(read > 0);
+        } while (read > 0);
 
 
         furi_string_trim(code);
         furi_string_trim(code);
-    } else {
+    }
+    else
+    {
         furi_string_set(code, "print('it works!')");
         furi_string_set(code, "print('it works!')");
     }
     }
 
 
@@ -77,54 +61,19 @@ static void load_python_file(FuriString* file_path, FuriString* code) {
     furi_record_close(RECORD_STORAGE);
     furi_record_close(RECORD_STORAGE);
 }
 }
 
 
-int32_t mp_flipper_app(void* p) {
+int32_t mp_flipper_app(void *p)
+{
     UNUSED(p);
     UNUSED(p);
 
 
-    mp_flipper_canvas = malloc(((SCREEN_WIDTH * SCREEN_HEIGHT) / SCREEN_PIXEL_PER_ITEM) * sizeof(uint32_t));
-
-    FuriString* file_path = furi_string_alloc();
-    FuriString* code = furi_string_alloc();
+    FuriString *file_path = furi_string_alloc();
+    FuriString *code = furi_string_alloc();
 
 
     load_python_file(file_path, code);
     load_python_file(file_path, code);
 
 
-    const size_t memory_size = memmgr_get_free_heap() * 0.5;
-    const size_t stack_size = 2 * 1024;
-    uint8_t* memory = malloc(memory_size * sizeof(uint8_t));
-
-    FURI_LOG_I(TAG, "allocated memory is %zu bytes", memory_size);
-    FURI_LOG_I(TAG, "stack size is %zu bytes", stack_size);
-    FURI_LOG_I(TAG, "executing python script %s", furi_string_get_cstr(file_path));
-
-    size_t index = furi_string_search_rchar(file_path, '/');
-
-    furi_check(index != FURI_STRING_FAILURE);
-
-    furi_string_left(file_path, index);
-
-    root_module_path = furi_string_get_cstr(file_path);
-
-    // initialize view port
-    mp_flipper_view_port = view_port_alloc();
-    view_port_draw_callback_set(mp_flipper_view_port, draw_callback, NULL);
-
-    // initialize GUI
-    Gui* gui = furi_record_open(RECORD_GUI);
-    gui_add_view_port(gui, mp_flipper_view_port, GuiLayerFullscreen);
-
-    mp_embed_init(memory + stack_size, memory_size - stack_size, memory);
-    mp_embed_exec_str(furi_string_get_cstr(code));
-    mp_embed_deinit();
-
-    // destroy GUI and view port
-    gui_remove_view_port(gui, mp_flipper_view_port);
-    view_port_free(mp_flipper_view_port);
-    furi_record_close(RECORD_GUI);
+    py_app((void *)furi_string_get_cstr(file_path));
 
 
     furi_string_free(file_path);
     furi_string_free(file_path);
     furi_string_free(code);
     furi_string_free(code);
-    free(memory);
-
-    free(mp_flipper_canvas);
 
 
     return 0;
     return 0;
 }
 }

+ 0 - 3
mp_flipper_app.h

@@ -4,13 +4,10 @@
 
 
 #include <gui/gui.h>
 #include <gui/gui.h>
 
 
-#define TAG "uPython"
-
 #define SCREEN_WIDTH 128
 #define SCREEN_WIDTH 128
 #define SCREEN_HEIGHT 64
 #define SCREEN_HEIGHT 64
 #define SCREEN_PIXEL_PER_ITEM 32
 #define SCREEN_PIXEL_PER_ITEM 32
 
 
-extern uint32_t* mp_flipper_canvas;
 extern ViewPort* mp_flipper_view_port;
 extern ViewPort* mp_flipper_view_port;
 extern FuriMessageQueue* mp_flipper_event_queue;
 extern FuriMessageQueue* mp_flipper_event_queue;
 
 

+ 115 - 0
mp_flipper_app_settings.c

@@ -0,0 +1,115 @@
+#include <gui/gui.h>
+#include <gui/modules/variable_item_list.h>
+#include <gui/view_dispatcher.h>
+#include <lib/toolbox/value_index.h>
+
+#include "py_app.h"
+
+typedef struct {
+    Gui* gui;
+    PyApp* settings;
+    ViewDispatcher* view_dispatcher;
+    VariableItemList* items;
+} PyAppSettings;
+
+#define STACK_SIZE 3
+const char* const stack_size_text[STACK_SIZE] = {
+    "512",
+    "1024",
+    "2048",
+};
+const int32_t stack_size[STACK_SIZE] = {
+    512,
+    1024,
+    2048,
+};
+
+#define HEAP_SIZE 5
+const char* const heap_size_text[HEAP_SIZE] = {
+    "10%",
+    "20%",
+    "30%",
+    "40%",
+    "50%",
+};
+const float heap_size[HEAP_SIZE] = {
+    0.1,
+    0.2,
+    0.3,
+    0.4,
+    0.5,
+};
+
+static void set_stack_size(VariableItem* item) {
+    PyAppSettings* app = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+
+    variable_item_set_current_value_text(item, stack_size_text[index]);
+    app->settings->stack_size = stack_size[index];
+}
+
+static void set_heap_size(VariableItem* item) {
+    PyAppSettings* app = variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+
+    variable_item_set_current_value_text(item, heap_size_text[index]);
+    app->settings->heap_size = heap_size[index];
+}
+
+static uint32_t py_app_settings_exit(void* context) {
+    UNUSED(context);
+    return VIEW_NONE;
+}
+
+static PyAppSettings* alloc_settings(void) {
+    PyAppSettings* app = malloc(sizeof(PyAppSettings));
+
+    app->gui = furi_record_open(RECORD_GUI);
+    app->settings = py_app_load_settings();
+    app->items = variable_item_list_alloc();
+
+    View* view = variable_item_list_get_view(app->items);
+
+    view_set_previous_callback(view, py_app_settings_exit);
+
+    VariableItem* item;
+    uint8_t value_index;
+
+    item = variable_item_list_add(app->items, "Stack Size", STACK_SIZE, set_stack_size, app);
+    value_index = value_index_int32(app->settings->stack_size, stack_size, STACK_SIZE);
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, stack_size_text[value_index]);
+
+    item = variable_item_list_add(app->items, "Heap Size", HEAP_SIZE, set_heap_size, app);
+    value_index = value_index_float(app->settings->heap_size, heap_size, HEAP_SIZE);
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, heap_size_text[value_index]);
+
+    app->view_dispatcher = view_dispatcher_alloc();
+    view_dispatcher_enable_queue(app->view_dispatcher);
+    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
+    view_dispatcher_add_view(app->view_dispatcher, 0, view);
+    view_dispatcher_switch_to_view(app->view_dispatcher, 0);
+
+    return app;
+}
+
+static void free_settings(PyAppSettings* app) {
+    view_dispatcher_remove_view(app->view_dispatcher, 0);
+    variable_item_list_free(app->items);
+    view_dispatcher_free(app->view_dispatcher);
+
+    free(app->settings);
+
+    furi_record_close(RECORD_GUI);
+    free(app);
+}
+
+int32_t py_app_settings_app(void* p) {
+    UNUSED(p);
+    PyAppSettings* app = alloc_settings();
+    view_dispatcher_run(app->view_dispatcher);
+    py_app_save_settings(app->settings);
+    free_settings(app);
+    return 0;
+}

+ 162 - 0
py_app.c

@@ -0,0 +1,162 @@
+#include <furi.h>
+#include <storage/storage.h>
+
+#include <mp_flipper_runtime.h>
+#include <mp_flipper_compiler.h>
+
+#include "py_app.h"
+
+#define PYTHON_SETTINGS_VERSION 0x01
+#define PYTHON_SETTINGS_PATH INT_PATH(".python.settings")
+
+int32_t py_app(void *arg)
+{
+    if (arg == NULL || strlen(arg) == 0)
+    {
+        return 0;
+    }
+
+    FuriString *file_path = furi_string_alloc_printf("%s", (const char *)arg);
+
+    py_app_file_execute(file_path);
+
+    furi_string_free(file_path);
+
+    return 0;
+}
+
+void py_app_file_execute(FuriString *file)
+{
+    size_t stack;
+
+    PyApp *app = py_app_alloc();
+    const char *path = furi_string_get_cstr(file);
+    FuriString *file_path = furi_string_alloc_printf("%s", path);
+
+    do
+    {
+        FURI_LOG_I(TAG, "executing script %s", path);
+
+        const size_t heap_size = memmgr_get_free_heap() * app->heap_size;
+        const size_t stack_size = app->stack_size;
+        uint8_t *heap = malloc(heap_size * sizeof(uint8_t));
+
+        FURI_LOG_D(TAG, "allocated memory is %zu bytes", heap_size);
+        FURI_LOG_D(TAG, "stack size is %zu bytes", stack_size);
+
+        size_t index = furi_string_search_rchar(file_path, '/');
+
+        furi_check(index != FURI_STRING_FAILURE);
+
+        bool is_py_file = furi_string_end_with_str(file_path, ".py");
+
+        furi_string_left(file_path, index);
+
+        mp_flipper_set_root_module_path(furi_string_get_cstr(file_path));
+
+        mp_flipper_init(heap, heap_size, stack_size, &stack);
+
+        if (is_py_file)
+        {
+            mp_flipper_exec_py_file(path);
+        }
+        else
+        {
+            mp_flipper_exec_mpy_file(path);
+        }
+
+        mp_flipper_deinit();
+
+        free(heap);
+    } while (false);
+
+    furi_string_free(file_path);
+
+    py_app_free(app);
+}
+
+PyApp *py_app_load_settings()
+{
+    File *file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
+
+    PyApp *settings = malloc(sizeof(PyApp));
+
+    const size_t settings_size = sizeof(PyApp);
+
+    FURI_LOG_I(TAG, "loading settings from \"%s\"", PYTHON_SETTINGS_PATH);
+    bool fs_result = storage_file_open(file, PYTHON_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
+
+    if (fs_result)
+    {
+        size_t bytes_count = storage_file_read(file, settings, settings_size);
+
+        fs_result = bytes_count == settings_size;
+    }
+
+    if (fs_result)
+    {
+        FURI_LOG_I(TAG, "load success");
+    }
+    else
+    {
+        FURI_LOG_E(TAG, "load failed, %s", storage_file_get_error_desc(file));
+
+        settings->heap_size = 0.3;
+        settings->stack_size = 1024;
+        settings->version = PYTHON_SETTINGS_VERSION;
+    }
+
+    if (fs_result && settings->version != PYTHON_SETTINGS_VERSION)
+    {
+        FURI_LOG_E(TAG, "version(%d != %d) mismatch", settings->version, PYTHON_SETTINGS_VERSION);
+    }
+
+    storage_file_close(file);
+    storage_file_free(file);
+
+    furi_record_close(RECORD_STORAGE);
+
+    return settings;
+};
+
+void py_app_save_settings(PyApp *settings)
+{
+    File *file = storage_file_alloc(furi_record_open(RECORD_STORAGE));
+
+    const size_t settings_size = sizeof(PyApp);
+
+    FURI_LOG_I(TAG, "saving settings to \"%s\"", PYTHON_SETTINGS_PATH);
+
+    bool fs_result = storage_file_open(file, PYTHON_SETTINGS_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS);
+
+    if (fs_result)
+    {
+        size_t bytes_count = storage_file_write(file, settings, settings_size);
+
+        fs_result = bytes_count == settings_size;
+    }
+
+    if (fs_result)
+    {
+        FURI_LOG_I(TAG, "save success");
+    }
+    else
+    {
+        FURI_LOG_E(TAG, "save failed, %s", storage_file_get_error_desc(file));
+    }
+
+    storage_file_close(file);
+    storage_file_free(file);
+
+    furi_record_close(RECORD_STORAGE);
+};
+
+PyApp *py_app_alloc()
+{
+    return py_app_load_settings();
+}
+
+void py_app_free(PyApp *app)
+{
+    free(app);
+}

+ 19 - 0
py_app.h

@@ -0,0 +1,19 @@
+#include <furi.h>
+
+#define TAG "uPython"
+
+typedef struct {
+    size_t stack_size;
+    float heap_size;
+    uint8_t version;
+} PyApp;
+
+int32_t py_app(void* arg);
+
+void py_app_file_execute(FuriString* file);
+
+PyApp* py_app_load_settings();
+void py_app_save_settings(PyApp* settings);
+
+PyApp* py_app_alloc();
+void py_app_free(PyApp* app);