diff --git a/main.cpp b/main.cpp
index f1eaf8221..8a278abd3 100644
--- a/main.cpp
+++ b/main.cpp
@@ -34,11 +34,14 @@ along with this program. If not, see .
#include "scheduler.h"
#include "osd.h"
#include "offload.h"
+#include "hardware.h"
const char *version = "$VER:" VDATE;
int main(int argc, char *argv[])
{
+ printf("ZAPAROO_T2 main_start_ms=%lu\n", GetTimer(0));
+
// Always pin main worker process to core #1 as core #0 is the
// hardware interrupt handler in Linux. This reduces idle latency
// in the main loop by about 6-7x.
diff --git a/scheduler.cpp b/scheduler.cpp
index 2553e1d58..715126383 100644
--- a/scheduler.cpp
+++ b/scheduler.cpp
@@ -1,5 +1,6 @@
#include "scheduler.h"
#include
+#include
#include "libco.h"
#include "menu.h"
#include "user_io.h"
@@ -86,6 +87,8 @@ void scheduler_run(void)
for (;;)
{
scheduler_schedule();
+ if (alt_launcher_scheduler_sleep_enabled())
+ usleep(100);
}
co_delete(co_ui);
diff --git a/support/zaparoo/alt_launcher.cpp b/support/zaparoo/alt_launcher.cpp
index 6408ff2c0..0f6b545bb 100644
--- a/support/zaparoo/alt_launcher.cpp
+++ b/support/zaparoo/alt_launcher.cpp
@@ -68,8 +68,9 @@ uint16_t alt_launcher_fb_terminal_key(uint32_t mask, bool osd_button)
static pid_t s_pid = 0;
static int s_crash_count = 0;
static unsigned long s_respawn_timer = 0;
+static unsigned long s_tty_deadline = 0;
static unsigned long s_native_status_timer = 0;
-static unsigned long s_native_fb_mode_timer = 0;
+static unsigned long s_native_crt_finish_timer = 0;
static bool s_gave_up = false;
static bool s_init_pending = false;
static bool s_native_crt = false;
@@ -81,6 +82,8 @@ static const char s_tty_path[] = "/dev/tty2";
static const char s_fb_mode_path[] = "/sys/module/MiSTer_fb/parameters/mode";
static const char s_crt_state_file[] = "zaparoo_launcher_crt.bin";
static const char s_offsets_file[] = "zaparoo_video_offsets.bin";
+static char s_saved_fb_mode[64];
+static bool s_saved_fb_mode_valid = false;
static int8_t s_h_offset = 0;
static int8_t s_v_offset = 0;
@@ -134,6 +137,51 @@ static void set_launcher_fb_mode(int fmt, int rb, int width, int height, int str
printf("alt_launcher: fb mode set to %dx%d fmt=%d stride=%d\n", width, height, fmt, stride);
}
+static void save_current_fb_mode(void)
+{
+ if (s_saved_fb_mode_valid)
+ return;
+
+ FILE *fp = fopen(s_fb_mode_path, "rt");
+ if (!fp)
+ {
+ printf("alt_launcher: unable to read fb mode: %s\n", strerror(errno));
+ return;
+ }
+
+ if (fgets(s_saved_fb_mode, sizeof(s_saved_fb_mode), fp))
+ {
+ size_t len = strlen(s_saved_fb_mode);
+ while (len && (s_saved_fb_mode[len - 1] == '\n' || s_saved_fb_mode[len - 1] == '\r'))
+ s_saved_fb_mode[--len] = 0;
+ s_saved_fb_mode_valid = len != 0;
+ if (s_saved_fb_mode_valid)
+ printf("alt_launcher: saved fb mode '%s'\n", s_saved_fb_mode);
+ }
+ fclose(fp);
+}
+
+static void restore_saved_fb_mode(void)
+{
+ if (!s_saved_fb_mode_valid)
+ {
+ set_launcher_fb_mode(8888, 1, 960, 720, 3840);
+ return;
+ }
+
+ FILE *fp = fopen(s_fb_mode_path, "wt");
+ if (!fp)
+ {
+ printf("alt_launcher: unable to restore fb mode: %s\n", strerror(errno));
+ return;
+ }
+
+ fprintf(fp, "%s\n", s_saved_fb_mode);
+ fclose(fp);
+ printf("alt_launcher: restored fb mode '%s'\n", s_saved_fb_mode);
+ s_saved_fb_mode_valid = false;
+}
+
static void set_native_crt_fb_mode(bool log = true)
{
set_launcher_fb_mode(8888, 1, 320, 240, 1280, log);
@@ -227,41 +275,34 @@ static void disable_native_crt_path(void)
user_io_status_set("[9]", 0);
video_fb_enable(0);
set_vga_fb(0);
- set_launcher_fb_mode(8888, 1, 960, 720, 3840);
+ restore_saved_fb_mode();
+ s_tty_deadline = 0;
s_native_status_timer = 0;
- s_native_fb_mode_timer = 0;
+ s_native_crt_finish_timer = 0;
}
-static void enable_native_crt_path(void)
+static void prepare_native_crt_path(void)
{
set_vga_fb(0);
video_fb_enable(0);
-
- // Double-write with a settle window so the kernel module's 320x240 layout
- // is live before status[9] flips. Without this, the frontend renders for
- // up to a second under stale dims (the post-fork retry timer used to be
- // what eventually fixed the picture).
+ save_current_fb_mode();
set_native_crt_fb_mode(false);
- usleep(100000);
- set_native_crt_fb_mode();
+ s_native_crt_finish_timer = GetTimer(1);
+ if (!s_native_crt_finish_timer) s_native_crt_finish_timer = 1;
+}
+
+static void finish_native_crt_path(void)
+{
+ // The frontend repeats the 320x240 linuxfb `vmode` while it starts. This
+ // side does not rewrite the mode again; it only blanks the native DDR
+ // scan-out buffer before status[9] routes the FPGA to it.
+ s_native_crt_finish_timer = 0;
blank_native_crt_fb();
user_io_status_set("[9]", 1);
s_native_status_timer = GetTimer(500);
if (!s_native_status_timer) s_native_status_timer = 1;
- s_native_fb_mode_timer = GetTimer(1000);
- if (!s_native_fb_mode_timer) s_native_fb_mode_timer = 1;
-}
-
-static void wait_launcher_tty_ready(pid_t pid)
-{
- for (int i = 0; i < 100; i++)
- {
- if (launcher_tty_ready(pid))
- return;
- usleep(10000);
- }
}
static void return_to_normal_mode(void)
@@ -282,6 +323,9 @@ static void reset_launcher_state(void)
{
s_pid = 0;
s_respawn_timer = 0;
+ s_tty_deadline = 0;
+ s_native_status_timer = 0;
+ s_native_crt_finish_timer = 0;
s_crash_count = 0;
s_gave_up = false;
s_init_pending = false;
@@ -337,34 +381,71 @@ static void release_launcher_video(void)
}
}
+static void exec_launcher_child(const char *path)
+{
+ setenv("LC_ALL", "en_US.UTF-8", 1);
+ setenv("HOME", "/root", 1);
+
+ cpu_set_t set;
+ CPU_ZERO(&set);
+ CPU_SET(0, &set);
+ sched_setaffinity(0, sizeof(set), &set);
+
+ setsid();
+
+ int tty_fd = open(s_tty_path, O_RDWR);
+ if (tty_fd >= 0)
+ {
+ ioctl(tty_fd, TIOCSCTTY, 0);
+ dup2(tty_fd, STDIN_FILENO);
+ dup2(tty_fd, STDOUT_FILENO);
+ dup2(tty_fd, STDERR_FILENO);
+ if (tty_fd > STDERR_FILENO)
+ close(tty_fd);
+ }
+
+ static const char clear[] = "\033[0m\033[?25l\033[37m\033[40m\033[2J\033[H";
+ if (write(STDOUT_FILENO, clear, sizeof(clear) - 1) < 0) {}
+
+ if (s_native_crt)
+ execl(path, path, "--crt", NULL);
+ else
+ execl(path, path, NULL);
+ _exit(1);
+}
+
+static void finalize_spawn(void)
+{
+ s_tty_deadline = 0;
+ video_chvt(s_vt);
+ if (!s_native_crt)
+ video_fb_enable(1);
+ else
+ {
+ input_switch(0);
+ user_io_status_set("[9]", 1);
+ }
+
+ // The frontend grabs input as soon as it starts. If the OSD is still
+ // up (e.g. user toggled CRT mode or hit Reboot from System Settings),
+ // it would trap input with no way to dismiss it — drop it now.
+ if (menu_present()) MenuHide();
+}
+
static void spawn(void)
{
char path[2100];
strncpy(path, getFullPath(s_launcher_path), sizeof(path) - 1);
path[sizeof(path) - 1] = '\0';
- static const char cmd[] =
- "#!/bin/bash\n"
- "export LC_ALL=en_US.UTF-8\n"
- "export HOME=/root\n"
- "printf '\\033[0m\\033[?25l\\033[37m\\033[40m\\033[2J\\033[H'\n"
- "if [ \"$ALT_LAUNCHER_CRT\" = \"1\" ]; then\n"
- " exec \"$ALT_LAUNCHER_PATH\" --crt\n"
- "fi\n"
- "exec \"$ALT_LAUNCHER_PATH\"\n";
-
- unlink("/tmp/alt_launcher");
- if (!FileSave("/tmp/alt_launcher", (void*)cmd, strlen(cmd)))
- return;
-
user_io_osd_key_enable(0);
clear_launcher_tty();
printf("alt_launcher: native_crt=%d\n", s_native_crt);
if (s_native_crt)
{
- enable_native_crt_path();
- printf("alt_launcher: native CRT path enabled\n");
+ prepare_native_crt_path();
+ printf("alt_launcher: native CRT path prepared\n");
}
else
{
@@ -384,32 +465,11 @@ static void spawn(void)
printf("alt_launcher: spawned pid=%d path=%s\n", s_pid, path);
if (!s_pid)
{
- setenv("ALT_LAUNCHER_PATH", path, 1);
- setenv("ALT_LAUNCHER_CRT", s_native_crt ? "1" : "0", 1);
- cpu_set_t set;
- CPU_ZERO(&set);
- CPU_SET(0, &set);
- sched_setaffinity(0, sizeof(set), &set);
- setsid();
- execl("/sbin/agetty", "/sbin/agetty", "-a", "root", "-l",
- "/tmp/alt_launcher", "-i", "--nohostname", "-L", s_tty, "linux", NULL);
- _exit(1);
+ exec_launcher_child(path);
}
- wait_launcher_tty_ready(s_pid);
- video_chvt(s_vt);
- if (!s_native_crt)
- video_fb_enable(1);
- else
- {
- input_switch(0);
- user_io_status_set("[9]", 1);
- }
-
- // The frontend grabs input as soon as it starts. If the OSD is still
- // up (e.g. user toggled CRT mode or hit Reboot from System Settings),
- // it would trap input with no way to dismiss it — drop it now.
- if (menu_present()) MenuHide();
+ s_tty_deadline = GetTimer(1000);
+ if (!s_tty_deadline) s_tty_deadline = 1;
}
bool alt_launcher_active(void)
@@ -417,6 +477,11 @@ bool alt_launcher_active(void)
return s_pid != 0;
}
+bool alt_launcher_scheduler_sleep_enabled(void)
+{
+ return s_pid || s_init_pending || s_respawn_timer || s_tty_deadline || s_native_crt_finish_timer;
+}
+
bool alt_launcher_native_crt(void)
{
return s_native_crt && s_pid != 0;
@@ -469,10 +534,23 @@ void alt_launcher_init(bool native_crt)
return;
s_crash_count = 0;
s_respawn_timer = 0;
+ s_tty_deadline = 0;
s_native_crt = native_crt;
s_init_pending = true;
}
+static void alt_launcher_start(bool native_crt)
+{
+ if (!alt_launcher_configured() || s_pid || s_gave_up)
+ return;
+ s_crash_count = 0;
+ s_respawn_timer = 0;
+ s_tty_deadline = 0;
+ s_native_crt = native_crt;
+ s_init_pending = false;
+ spawn();
+}
+
void alt_launcher_prepare_for_script(void)
{
reset_launcher_tty();
@@ -486,6 +564,7 @@ void alt_launcher_prepare_for_script(void)
wait_launcher_stopped(pid);
user_io_osd_key_enable(1);
s_respawn_timer = 0;
+ s_tty_deadline = 0;
s_crash_count = 0;
s_init_pending = false;
s_gave_up = false;
@@ -514,6 +593,12 @@ void alt_launcher_poll(void)
{
if (s_pid)
{
+ if (s_native_crt && s_native_crt_finish_timer && CheckTimer(s_native_crt_finish_timer))
+ {
+ finish_native_crt_path();
+ printf("alt_launcher: native CRT path enabled\n");
+ }
+
if (s_native_crt && s_native_status_timer && CheckTimer(s_native_status_timer))
{
user_io_status_set("[9]", 1);
@@ -521,16 +606,11 @@ void alt_launcher_poll(void)
if (!s_native_status_timer) s_native_status_timer = 1;
}
- if (s_native_crt && s_native_fb_mode_timer && CheckTimer(s_native_fb_mode_timer))
- {
- set_native_crt_fb_mode();
- s_native_fb_mode_timer = 0;
- }
-
int status;
if (waitpid(s_pid, &status, WNOHANG) == s_pid)
{
s_pid = 0;
+ s_tty_deadline = 0;
user_io_osd_key_enable(1);
bool exited = WIFEXITED(status);
int exit_status = exited ? WEXITSTATUS(status) : 0;
@@ -565,7 +645,11 @@ void alt_launcher_poll(void)
s_crash_count = 0;
s_respawn_timer = GetTimer(1000);
if (!s_respawn_timer) s_respawn_timer = 1;
+ return;
}
+
+ if (s_tty_deadline && !s_native_crt_finish_timer && (launcher_tty_ready(s_pid) || CheckTimer(s_tty_deadline)))
+ finalize_spawn();
return;
}
@@ -654,7 +738,7 @@ void zaparoo_alt_launcher_init_for_core(void)
}
}
-void zaparoo_alt_launcher_init_for_menu(void)
+static void zaparoo_alt_launcher_prepare_menu_state(bool start)
{
bool crt = load_persisted_native_crt();
load_persisted_offsets();
@@ -663,5 +747,16 @@ void zaparoo_alt_launcher_init_for_menu(void)
// Push the persisted offsets to the FPGA now that the menu RBF is loaded.
user_io_status_set("[13:10]", (uint32_t)((uint8_t)s_h_offset & 0xF));
user_io_status_set("[17:14]", (uint32_t)((uint8_t)s_v_offset & 0xF));
- alt_launcher_init(crt);
+ if (start) alt_launcher_start(crt);
+ else alt_launcher_init(crt);
+}
+
+void zaparoo_alt_launcher_init_for_menu(void)
+{
+ zaparoo_alt_launcher_prepare_menu_state(false);
+}
+
+void zaparoo_alt_launcher_start_for_menu(void)
+{
+ zaparoo_alt_launcher_prepare_menu_state(true);
}
diff --git a/support/zaparoo/alt_launcher.h b/support/zaparoo/alt_launcher.h
index 3f8aad78a..4eeabf660 100644
--- a/support/zaparoo/alt_launcher.h
+++ b/support/zaparoo/alt_launcher.h
@@ -13,6 +13,7 @@ void alt_launcher_resume_after_script(void);
bool alt_launcher_native_crt(void);
bool alt_launcher_active(void);
bool alt_launcher_configured(void);
+bool alt_launcher_scheduler_sleep_enabled(void);
// Display centering: signed offsets clamped to -8..+7. Setters update the
// in-memory cache, persist to the config dir, and push to the FPGA via
@@ -28,3 +29,4 @@ uint16_t alt_launcher_fb_terminal_key(uint32_t mask, bool osd_button);
bool zaparoo_is_native_core(void);
void zaparoo_alt_launcher_init_for_core(void);
void zaparoo_alt_launcher_init_for_menu(void);
+void zaparoo_alt_launcher_start_for_menu(void);
diff --git a/user_io.cpp b/user_io.cpp
index 61300d83a..05581bd61 100644
--- a/user_io.cpp
+++ b/user_io.cpp
@@ -1469,7 +1469,8 @@ void user_io_init(const char *path, const char *xml)
// Zaparoo: u-boot/stock binary may have loaded the system menu.rbf before we got here.
// Force a reload of our hardcoded menu RBF if we booted without an explicit RBF path.
- if (is_menu() && !rbf_path[0]) fpga_load_rbf(menu_rbf_name());
+ if (is_menu() && !rbf_path[0] && !zaparoo_is_native_core()) fpga_load_rbf(menu_rbf_name());
+ else if (is_menu() && !rbf_path[0] && zaparoo_is_native_core()) zaparoo_alt_launcher_start_for_menu();
uint8_t hotswap[4] = {};
ide_reset(hotswap);
@@ -1545,7 +1546,10 @@ void user_io_init(const char *path, const char *xml)
else if (is_menu())
{
user_io_status_set("[4]", (cfg.menu_pal) ? 1 : 0);
- if (alt_launcher_configured()) zaparoo_alt_launcher_init_for_menu();
+ if (alt_launcher_configured())
+ {
+ if (rbf_path[0] || !zaparoo_is_native_core()) zaparoo_alt_launcher_init_for_menu();
+ }
else
if (cfg.fb_terminal) video_menu_bg(user_io_status_get("[3:1]"));
else user_io_status_set("[3:1]", 0);