From bc8a76a773b89f3f8f64db2c3c53a04eb0dc176c Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Mon, 9 Mar 2026 12:39:19 +0100 Subject: [PATCH] Initial MPong game commit This demonstrates native machine code in .mpy files as documented in https://docs.micropython.org/en/latest/develop/natmod.html The idea is to make a simple Pong game to demonstrate input from buttons and touchscreen and output to framebuffer and audio. --- c_mpos/mpong/Makefile | 0 c_mpos/mpong/Makefile_amd64 | 13 +++ c_mpos/mpong/Makefile_esp32 | 13 +++ c_mpos/mpong/build_amd64.sh | 11 ++ c_mpos/mpong/build_esp32.sh | 13 +++ c_mpos/mpong/mpong.c | 103 ++++++++++++++++++ .../META-INF/MANIFEST.JSON | 24 ++++ .../com.micropythonos.mpong/assets/mpong.py | 62 +++++++++++ .../res/mipmap-mdpi/icon_64x64.png | Bin 0 -> 7933 bytes 9 files changed, 239 insertions(+) create mode 100644 c_mpos/mpong/Makefile create mode 100644 c_mpos/mpong/Makefile_amd64 create mode 100644 c_mpos/mpong/Makefile_esp32 create mode 100755 c_mpos/mpong/build_amd64.sh create mode 100755 c_mpos/mpong/build_esp32.sh create mode 100644 c_mpos/mpong/mpong.c create mode 100644 internal_filesystem/apps/com.micropythonos.mpong/META-INF/MANIFEST.JSON create mode 100644 internal_filesystem/apps/com.micropythonos.mpong/assets/mpong.py create mode 100644 internal_filesystem/apps/com.micropythonos.mpong/res/mipmap-mdpi/icon_64x64.png diff --git a/c_mpos/mpong/Makefile b/c_mpos/mpong/Makefile new file mode 100644 index 00000000..e69de29b diff --git a/c_mpos/mpong/Makefile_amd64 b/c_mpos/mpong/Makefile_amd64 new file mode 100644 index 00000000..89e8fbc0 --- /dev/null +++ b/c_mpos/mpong/Makefile_amd64 @@ -0,0 +1,13 @@ +MPY_DIR = ../../lvgl_micropython/lib/micropython/ + +# Name of module +MOD = mpong + +# Source files (.c or .py) +SRC = mpong.c + +# Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin, rv32imc, rv64imc) +ARCH = x64 + +# Include to get the rules for compiling and linking the module +include $(MPY_DIR)/py/dynruntime.mk diff --git a/c_mpos/mpong/Makefile_esp32 b/c_mpos/mpong/Makefile_esp32 new file mode 100644 index 00000000..557a76b3 --- /dev/null +++ b/c_mpos/mpong/Makefile_esp32 @@ -0,0 +1,13 @@ +MPY_DIR = ../../lvgl_micropython/lib/micropython/ + +# Name of module +MOD = mpong + +# Source files (.c or .py) +SRC = mpong.c + +# Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin, rv32imc, rv64imc) +ARCH = xtensawin + +# Include to get the rules for compiling and linking the module +include $(MPY_DIR)/py/dynruntime.mk diff --git a/c_mpos/mpong/build_amd64.sh b/c_mpos/mpong/build_amd64.sh new file mode 100755 index 00000000..26d77aaa --- /dev/null +++ b/c_mpos/mpong/build_amd64.sh @@ -0,0 +1,11 @@ +mydir=$(readlink -f "$0") +mydir=$(dirname "$mydir") + +cd "$mydir" +rm -rf build + +make -f Makefile_amd64 = fill_pixels) { + break; + } + pixels[idx] = 0x0000; // RGB565 black + } + } + + // Advance the line for the next call, wrapping at the bottom. + g_line_y++; + if (g_line_y >= height) { + g_line_y = 0; + } + + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_0(render_obj, render); + +// move_paddle(delta): print delta for debugging. +static mp_obj_t move_paddle(mp_obj_t delta_obj) { + int delta = mp_obj_get_int(delta_obj); + mp_printf(&mp_plat_print, "delta: %d\n", delta); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(move_paddle_obj, move_paddle); + +// This is the entry point and is called when the module is imported +mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) { + // This must be first, it sets up the globals dict and other things + MP_DYNRUNTIME_INIT_ENTRY + + // Make the function available in the module's namespace + mp_store_global(MP_QSTR_init, MP_OBJ_FROM_PTR(&init_obj)); + mp_store_global(MP_QSTR_render, MP_OBJ_FROM_PTR(&render_obj)); + mp_store_global(MP_QSTR_move_paddle, MP_OBJ_FROM_PTR(&move_paddle_obj)); + mp_store_global(MP_QSTR_readfile, MP_OBJ_FROM_PTR(&readfile_obj)); + + // This must be last, it restores the globals dict + MP_DYNRUNTIME_INIT_EXIT +} diff --git a/internal_filesystem/apps/com.micropythonos.mpong/META-INF/MANIFEST.JSON b/internal_filesystem/apps/com.micropythonos.mpong/META-INF/MANIFEST.JSON new file mode 100644 index 00000000..55463a99 --- /dev/null +++ b/internal_filesystem/apps/com.micropythonos.mpong/META-INF/MANIFEST.JSON @@ -0,0 +1,24 @@ +{ +"name": "mPong", +"publisher": "MicroPythonOS", +"short_description": "PingPong game", +"long_description": "Demonstrates native machinecode in .mpy files on both AMD64 and ESP32", +"icon_url": "https://apps.micropythonos.com/apps/com.micropythonos.mpong/icons/com.micropythonos.mpong_0.1.0_64x64.png", +"download_url": "https://apps.micropythonos.com/apps/com.micropythonos.mpong/mpks/com.micropythonos.mpong_0.1.0.mpk", +"fullname": "com.micropythonos.mpong", +"version": "0.1.0", +"category": "games", +"activities": [ + { + "entrypoint": "assets/mpong.py", + "classname": "MPong", + "intent_filters": [ + { + "action": "main", + "category": "launcher" + } + ] + } + ] +} + diff --git a/internal_filesystem/apps/com.micropythonos.mpong/assets/mpong.py b/internal_filesystem/apps/com.micropythonos.mpong/assets/mpong.py new file mode 100644 index 00000000..2e61fc5d --- /dev/null +++ b/internal_filesystem/apps/com.micropythonos.mpong/assets/mpong.py @@ -0,0 +1,62 @@ +import lvgl as lv +from mpos import Activity, DisplayMetrics, InputManager + +indev_error_x = 160 +indev_error_y = 120 + +DARKPINK = lv.color_hex(0xEC048C) + +import sys +if sys.platform == "esp32": + import mpong_esp32 as mpong +else: + import mpong_amd64 as mpong + +class MPong(Activity): + + hor_res = 0 + ver_res = 0 + layer = None + buffer = None + + # Widgets: + screen = None + canvas = None + + def onCreate(self): + self.screen = lv.obj() + self.canvas = lv.canvas(self.screen) + d = lv.display_get_default() + self.hor_res = d.get_horizontal_resolution() + self.ver_res = d.get_vertical_resolution() + self.canvas.set_size(self.hor_res, self.ver_res) + #self.canvas.set_style_bg_color(lv.color_white(), lv.PART.MAIN) + self.buffer = bytearray(self.hor_res * self.ver_res * 2) + self.canvas.set_buffer(self.buffer, self.hor_res, self.ver_res, lv.COLOR_FORMAT.NATIVE) + #self.canvas.fill_bg(lv.color_white(), lv.OPA.COVER) + self.canvas.add_flag(lv.obj.FLAG.CLICKABLE) + self.canvas.add_event_cb(self.touch_cb, lv.EVENT.ALL, None) + self.layer = lv.layer_t() + self.canvas.init_layer(self.layer) + self.setContentView(self.screen) + + def onResume(self, screen): + mpong.init(self.buffer, self.hor_res, self.ver_res) + self.refresh_timer = lv.timer_create(self.run_mpong, 10, None) + + def onPause(self, screen): + print("stopping it!") + if self.refresh_timer: + self.refresh_timer.delete() + + def run_mpong(self, timer=None): + mpong.render() + self.canvas.invalidate() # force redraw + + def touch_cb(self, event): + event_code=event.get_code() + if event_code not in [19,23,25,26,27,28,29,30,49]: + if event_code == lv.EVENT.PRESSING: # this is probably enough + x, y = InputManager.pointer_xy() + mpong.move_paddle(-10) + return diff --git a/internal_filesystem/apps/com.micropythonos.mpong/res/mipmap-mdpi/icon_64x64.png b/internal_filesystem/apps/com.micropythonos.mpong/res/mipmap-mdpi/icon_64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..30c6a2d7ecf9cdecd364294aad95f430b33a6868 GIT binary patch literal 7933 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 literal 0 HcmV?d00001