--- name: schunix-boot-splash-logo description: "The boot logo pipeline, why the loader draws it rather than the kernel, and the STAGE2_SECTORS trap that consolidating it exposed." metadata: node_type: memory type: project originSessionId: 9d8a2aef-bcf0-47a0-96c6-f706efab4a95 modified: 2026-08-08T12:04:49.027Z --- The boot splash shows the `Assets/Schunix Small Logo.png` mark (an SX monogram in a ring) above the `S C H U N I X` wordmark and its accent rule, all centred as one group. Verified rendering correctly on 2026-08-08. **The logo is drawn by the bootloader, not the kernel.** This is the counter-intuitive part and it is a timing fact, not a preference: the kernel does not reach `vga_init()` until roughly the last 300ms of a ~2.8s boot, so a kernel-drawn logo appeared for about one frame before `console_takeover()` wiped it. Drawn from `splash()` in `boot/stage2main.c` it is up for the whole boot. `kernel/dev/vga.c` keeps the same drawing code against the same mask (`vga_draw_logo`, `vga_logo_centered`) for things that want the mark *after* boot — the crash screen, eventually `schinit`. Don't "fix" the duplication by deleting the loader's copy. **Pipeline:** `tools/mklogo.py` (host Python, has its own PNG inflate + un-filter) crops to the mark, box-downscales, normalises to full strength, packs to a 4-bit coverage mask, and writes `include/logo.h`. Run `make logo` — it is *not* a build dependency, and `include/logo.h` is committed, on the same reasoning as `include/font.h`. The mask is coverage, not colour, so the palette stays in [[schunix-console-tty]]'s `include/theme.h`; the loader composites it in `SCH_FG` because the source art is a silver gradient and the accent is reserved for the one rule. 4 bits rather than 8 because the header is included by the *bootloader*, where size is a boot constraint. **`STAGE2_SECTORS` was encoded in five places, not the three `docs/guides/layout.md` claimed.** Raising it 24 → 48 to fit the 8 KiB mask proved it: the `dd seek=25` offset stayed behind and the kernel got written straight over stage2's tail. The five were `boot/bootasm.S`, `boot/stage2main.c`, the `dd seek=`, and *twice* inside `tools/mkiso.sh` (its size assertion and `-boot-load-size`). It now lives only in the root Makefile, which passes `-DSTAGE2_SECTORS` and exports it to mkiso; the `#define`s left in the boot sources are `#ifndef` fallbacks. Hard ceiling is 65 sectors — stage2 loads at `0x7E00` and must not reach the multiboot kernel-search buffer at `0x10000` — and `stage2main.c` now static-asserts that. See also [[schunix-iso-boot]]. **`tools/splashshot.ps1`** captures a burst of QEMU screendumps across early boot and reports each frame's distinct-colour count, because the splash window is too short to hit with a single guessed delay. It uses `-snapshot` plus `file.locking=off` so it runs while an interactive QEMU has the images open — booting from copies also works but means duplicating all 153 MB of `build/fs.img` per run. Colour counts are the assertion: ~2 means text console or blank, 18 means the 4bpp logo is up. If it ever reads 156 again, something is drawing an 8-bit mask. **Two traps, both hit while doing this:** - `$(shell expr $(N) \* 512)` in a Makefile does not work — make passes the backslash through, sh globs the `*`, and expr receives a directory listing. Make silently carries on with nonsense. Use `$$(( ))` in the recipe. - `make iso` was failing when this was written, for unrelated reasons since fixed — see [[schunix-ramdisk-root-sizing]]. `-boot-load-size 49` is now verified: the ISO boots from optical media only.