@@ -7,7 +7,9 @@ repo) has the full background and rationale if you want it.
77FPGA side of everything below is implemented, simulated, and pushed. The
88launcher work in this brief is the only remaining piece.
99** Existing code this modifies:** ` src/app/native_video_writer.cpp ` and the
10- ` --crt ` startup path in zaparoo-launcher (see also its ` docs/native-core-poc.md ` ).
10+ ` --crt ` startup path in zaparoo-launcher (see also its ` docs/native-core-poc.md ` ),
11+ plus ` support/zaparoo/alt_launcher.cpp ` / ` launcher_pages.cpp ` in the
12+ Main_MiSTer fork (section 3).
1113
1214---
1315
@@ -24,10 +26,13 @@ control block you already write**. Key consequences for the app:
2426 correctly calibrated CRT.
2527- The picture now * overscans* like broadcast TV: the outer few percent of the
2628 framebuffer is cropped on most sets. The UI must adopt safe-area rules
27- (section 5) — this is as much a part of the fix as the FPGA work.
28- - There is no "CRT mode" toggle anywhere. ** Publishing frames IS the mode
29- switch** : the core shows its noise pattern until your control word goes
30- live and reverts when you zero it.
29+ (section 6) — this is as much a part of the fix as the FPGA work.
30+ - The * core-side* CRT enable is gone: the new core has no ` status[9] ` bit
31+ and no OSD video options. ** Publishing frames IS the core's mode switch** :
32+ it shows its noise pattern until your control word goes live and reverts
33+ when you zero it. The * app-level* CRT mode (the ` --crt ` startup path:
34+ pixel fonts, CRT layout, DDR writer) is unchanged and very much stays —
35+ see section 3 for how it's coordinated now.
3136- Two new modes exist when you're ready for them: ** 720x480i60** (mode 1) and
3237 ** 352x288p50 PAL** (mode 2). The core side is done; you opt in per-frame
3338 via the mode field.
@@ -87,7 +92,53 @@ Protocol rules:
8792 core extracts fields itself (reads source line ` 2*line + field ` ). No
8893 field splitting, no half-frame timing on the ARM side.
8994
90- ## 3. Task 1 — Phase A (required): 352x240 writer + safe-area UI
95+ ## 3. ARM-side coordination: who turns CRT mode on
96+
97+ "CRT mode" remains a real mode of the * app* : it decides whether the launcher
98+ renders pixel fonts and CRT layout into the DDR writer (` --crt ` ) or runs the
99+ normal HDMI/scaler path. The Main_MiSTer fork already owns that decision and
100+ the mechanism survives v2 almost unchanged:
101+
102+ - ** Persisted state:** ` config/zaparoo_launcher_crt.bin ` (1-byte bool,
103+ written via ` FileSaveConfig ` ). Main reads it when the menu core loads
104+ (` zaparoo_alt_launcher_init_for_menu() ` in ` support/zaparoo/alt_launcher.cpp ` )
105+ and spawns the frontend with or without ` --crt ` .
106+ - ** Toggling:** the OSD "Zaparoo Frontend → Video" page calls
107+ ` alt_launcher_toggle_crt() ` , which persists the new value, SIGTERMs the
108+ frontend, and respawns it with the new flag. ** No Main restart is needed**
109+ — only the frontend process bounces. Keep this; a full Main re-exec is
110+ strictly worse (slower, drops core state) and buys nothing.
111+
112+ What v2 changes in Main (these are required Main-fork edits, same effort
113+ bucket as Task 1):
114+
115+ 1 . ` user_io_status_set("[9]", …) ` everywhere in ` alt_launcher.cpp ` is now a
116+ no-op — the new core has no CRT status bit. Delete the writes and the
117+ 500 ms re-assert timer. The frontend publishing word0/word1 * is* the
118+ enable; Main's job shrinks to fb-mode setup, blanking, and spawning.
119+ 2 . The H/V offset status writes (` [13:10] ` /` [17:14] ` ) are dead too. Offsets
120+ move into DDR word1, which only the frontend writes. Remove the OSD
121+ "H Offset"/"V Offset" entries in ` launcher_pages.cpp ` and the
122+ ` zaparoo_video_offsets.bin ` handling; the launcher owns centering now
123+ (section 7). Optional nicety: on first run, the launcher migrates the
124+ two bytes from ` config/zaparoo_video_offsets.bin ` into its own config so
125+ existing users keep their calibration.
126+ 3 . ` set_native_crt_fb_mode() ` : 320x240 stride 1280 → ** 352x240 stride 1408** .
127+ 4 . ` blank_native_crt_fb() ` : region size 0xA0000 → ** 0x300000** . Under v2,
128+ zeroing the region isn't just ghost-clearing — a zeroed word0 means
129+ "writer stopped", so the blank deterministically parks the core on its
130+ noise pattern until the new frontend instance publishes.
131+
132+ Open choice (pick during implementation): if the CRT toggle should also live
133+ in the launcher's own settings UI, don't have the launcher restart Main.
134+ Instead: launcher writes ` zaparoo_launcher_crt.bin ` itself and exits with a
135+ reserved exit code (e.g. 42 = "re-read CRT config and respawn me"); Main's
136+ ` alt_launcher_poll() ` exit handler treats that code as a respawn-with-reload
137+ instead of ` return_to_normal_mode() ` . That's a ~ 10-line Main change and
138+ reuses the existing respawn machinery. The OSD toggle can stay as a second
139+ entry point — both paths converge on the same persisted bool + respawn.
140+
141+ ## 4. Task 1 — Phase A (required): 352x240 writer + safe-area UI
91142
92143This is the must-ship piece; modes 1 and 2 are follow-ups.
93144
@@ -98,14 +149,14 @@ This is the must-ship piece; modes 1 and 2 are follow-ups.
98149 buffers at ` +0x1000 ` / ` +0x180000 ` , mmap 0x300000.
991503 . Write word1 on init: magic ` 0x5A50 ` , mode 0, offsets from launcher config
100151 (default 0/0). Clear both words on stop.
101- 4 . UI safe-area pass (section 5 ).
102- 5 . Calibration screen (section 6 ).
152+ 4 . UI safe-area pass (section 6 ).
153+ 5 . Calibration screen (section 7 ).
103154
104155Acceptance: on hardware with the new core, the launcher UI fills a CRT
105156edge-to-edge; killing the launcher returns the noise pattern; a capture
106157device reports 15.734 kHz / 240p.
107158
108- ## 4 . Tasks 2 & 3 — PAL and 480i (when ready)
159+ ## 5 . Tasks 2 & 3 — PAL and 480i (when ready)
109160
110161** PAL (mode 2):** add a "video standard: NTSC / PAL" user setting. PAL
111162renders ** 352x288** and publishes mode 2. Note most PAL sets accept 60 Hz
@@ -114,9 +165,9 @@ mode 2 is for strict-50 Hz sets and correct-speed feel.
114165
115166** 480i (mode 1):** add a 720x480 rendering path and (optionally) per-screen
116167mode selection — e.g. main UI in 240p, text-heavy screens in 480i.
117- Flicker discipline is mandatory (section 5 , rule 4).
168+ Flicker discipline is mandatory (section 6 , rule 4).
118169
119- ## 5 . UI rendering rules (apply to every mode)
170+ ## 6 . UI rendering rules (apply to every mode)
120171
121172These are not suggestions; geometry alone doesn't fix "every CRT crops
122173differently":
@@ -138,9 +189,10 @@ differently":
138189 standard console-era 480i dashboard trick). Existing CRT typography rules
139190 in ` native-core-poc.md ` (integer snapping, bitmap fonts) stay in force.
140191
141- ## 6 . Calibration screen
192+ ## 7 . Calibration screen
142193
143- The launcher now owns centering (the OSD options are gone):
194+ The launcher now owns centering (the core's status bits are gone and Main's
195+ OSD offset entries go with them — see section 3, item 2):
144196
145197- Draw a border test pattern (240p-test-suite style: 1-px frame at the
146198 extreme edge, rectangles at the 90% and 80% safe areas, cross-hatch).
@@ -150,7 +202,7 @@ The launcher now owns centering (the OSD options are gone):
150202 zero — the standard timing is the centering mechanism, trims only
151203 compensate for miscentered sets.
152204
153- ## 7 . Verification checklist (frontend-visible items)
205+ ## 8 . Verification checklist (frontend-visible items)
154206
155207- Fill/centering on ** 2–3 different CRTs** plus a capture device (should
156208 report 15.734 kHz exactly; 480i should be detected as 480i, not 240p).
@@ -164,7 +216,7 @@ The launcher now owns centering (the OSD options are gone):
164216- HDMI output still locks in every mode (the core's ascal path handles it;
165217 just confirm).
166218
167- ## 8 . Reference
219+ ## 9 . Reference
168220
169221- FPGA-side spec and rationale: ` docs/native-video-plan.md ` (Menu_MiSTer).
170222- RTL that consumes this contract: ` rtl/native_video_reader.sv ` (the word1
0 commit comments