From 8b3a18fa0ca0ae89a8d27e64cfb9e3691f9a2432 Mon Sep 17 00:00:00 2001 From: Vladimir Smitka Date: Fri, 24 Jul 2026 09:52:31 +0000 Subject: [PATCH 1/4] py/compile: guard the WASM native-emitter table slot for mpy-cross The MICROPY_DYNAMIC_COMPILER emit_native_table lists every arch's method table so -march can pick one at runtime. mpy-cross links this table but cannot provide the WASM native emitter: it needs the port-runtime symbol mp_wasm_compile_native, which only exists in the wasm port, not the host tool. So building mpy-cross fails with: undefined reference to `emit_native_wasm_method_table' Gate the table entry on MICROPY_EMIT_WASM (NULL when absent). The slot stays index-aligned to MP_NATIVE_ARCH_WASM32, and mpy-cross never selects wasm32 for freezing, so the NULL is unreachable there. --- py/compile.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/py/compile.c b/py/compile.c index 8644ad58606..0df255da720 100644 --- a/py/compile.c +++ b/py/compile.c @@ -103,7 +103,11 @@ static const emit_method_table_t *emit_native_table[] = { &emit_native_xtensa_method_table, &emit_native_xtensawin_method_table, &emit_native_rv32_method_table, + #if MICROPY_EMIT_WASM &emit_native_wasm_method_table, + #else + NULL, // wasm native emitter is a PORT feature (needs mp_wasm_compile_native); absent in host mpy-cross + #endif &emit_native_debug_method_table, }; From 68e983d1b06f0ace6bbf2b08d451dbabdcd45a61 Mon Sep 17 00:00:00 2001 From: Vladimir Smitka Date: Fri, 24 Jul 2026 09:52:31 +0000 Subject: [PATCH 2/4] tools/gen_display_resources: support the current adafruit_bitmap_font API The display-resource generator targeted an old bitmap_font API and broke the wasm browser build: - BitmapStub only had _load_row(); current bitmap_font writes glyph pixels via bitmap[y*width + x] = bit -> "BitmapStub does not support item assignment". - glyphs are accessed as dicts (g["shift"], g["bounds"], g["bitmap"]) but fontio.Glyph is an object -> "'Glyph' object is not subscriptable". Give BitmapStub __setitem__ (flat index -> byte-packed bit) plus a byte-packed .rows property (kept _load_row for the older API), and read glyph attributes (g.shift_y, g.width/height/dx/dy, g.bitmap). --- tools/gen_display_resources.py | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tools/gen_display_resources.py b/tools/gen_display_resources.py index 9ed829f7d37..fbc26f7b94e 100644 --- a/tools/gen_display_resources.py +++ b/tools/gen_display_resources.py @@ -29,10 +29,26 @@ class BitmapStub: def __init__(self, width, height, color_depth): self.width = width - self.rows = [b""] * height + self.height = height + self._stride = (width + 7) // 8 # bytes per row, MSB-first byte-packed + self._buf = bytearray(self._stride * height) - def _load_row(self, y, row): - self.rows[y] = bytes(row) + def _load_row(self, y, row): # old bitmap_font API (row = packed bytes) + b = bytes(row) + self._buf[y * self._stride:y * self._stride + self._stride] = b[:self._stride] + + def __setitem__(self, index, value): # new bitmap_font API: flat y*width+x -> 0/1 + y, x = divmod(index, self.width) + off = y * self._stride + (x >> 3) + if value: + self._buf[off] |= 1 << (7 - (x & 7)) + else: + self._buf[off] &= ~(1 << (7 - (x & 7))) & 0xFF + + @property + def rows(self): + s = self._stride + return [bytes(self._buf[y * s:y * s + s]) for y in range(self.height)] f = bitmap_font.load_font(args.font, BitmapStub) @@ -69,7 +85,7 @@ def _load_row(self, y, row): filtered_characters = filtered_characters.replace(c, "") continue g = f.get_glyph(ord(c)) - if g["shift"][1] != 0: + if g.shift_y != 0: raise RuntimeError("y shift") if missing > 0: @@ -83,10 +99,10 @@ def _load_row(self, y, row): for x, c in enumerate(filtered_characters): g = f.get_glyph(ord(c)) - start_bit = x * tile_x + g["bounds"][2] - start_y = (tile_y - 2) - (g["bounds"][1] + g["bounds"][3]) - for y, row in enumerate(g["bitmap"].rows): - for i in range(g["bounds"][0]): + start_bit = x * tile_x + g.dx + start_y = (tile_y - 2) - (g.height + g.dy) + for y, row in enumerate(g.bitmap.rows): + for i in range(g.width): byte = i // 8 bit = i % 8 if row[byte] & (1 << (7 - bit)) != 0: From ce40b1bc35a339ff3194c84d4eaa6d08a398bd87 Mon Sep 17 00:00:00 2001 From: Vladimir Smitka Date: Fri, 24 Jul 2026 09:52:31 +0000 Subject: [PATCH 3/4] requirements-dev: add huffman + adafruit-circuitpython-bitmap-font The wasm port's tools/gen_display_resources.py imports both (huffman for the translation compression, adafruit_bitmap_font to rasterize the built-in font), but neither was listed, so a fresh venv fails the browser build with ModuleNotFoundError. --- requirements-dev.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/requirements-dev.txt b/requirements-dev.txt index 6a33c49daec..f676ff3783c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -31,6 +31,10 @@ cryptography minify_html jsmin +# for the wasm port's display-resource generation (tools/gen_display_resources.py) +huffman +adafruit-circuitpython-bitmap-font + # for Silicon Labs Configurator (SLC) websockets colorama From cee0d6427037926a444b72a59239928304c1e44a Mon Sep 17 00:00:00 2001 From: Vladimir Smitka Date: Fri, 24 Jul 2026 09:52:31 +0000 Subject: [PATCH 4/4] wasm/js/display: set the canvas backing-store resolution from the framebuffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Display only set canvas.style.width/height (CSS), not canvas.width/height (the backing-store resolution). An embedder whose lacks width/height attributes gets the 300x150 default, so putImageData() of the fbWidth×fbHeight framebuffer is clipped/misplaced (the built-in IDE happens to hardcode the attributes). Set canvas.width/height to the framebuffer dimensions so any embedder renders correctly; CSS still scales it for display. --- ports/wasm/js/display.mjs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ports/wasm/js/display.mjs b/ports/wasm/js/display.mjs index 5a9616113ae..08472f0dcfb 100644 --- a/ports/wasm/js/display.mjs +++ b/ports/wasm/js/display.mjs @@ -27,8 +27,13 @@ export class Display { ? this._ctx.createImageData(this._fbWidth, this._fbHeight) : null; - // Scale canvas to match displayio + // Match the canvas to the framebuffer. The backing-store resolution + // (width/height attributes) MUST equal the framebuffer, else + // putImageData() of a fbWidth×fbHeight ImageData is clipped to the + // canvas default (300×150). CSS width/height then scale it for display. if (canvas) { + canvas.width = this._fbWidth; + canvas.height = this._fbHeight; canvas.style.width = this._fbWidth + 'px'; canvas.style.height = this._fbHeight + 'px'; }