aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.c
diff options
context:
space:
mode:
authorgbc dev <gbc@localhost>2026-07-15 23:44:28 +0200
committergbc dev <gbc@localhost>2026-07-15 23:44:28 +0200
commit24a015ff047d4b7bb955ba4bc61d5793d7492878 (patch)
treeebaf323a5c530400c81f9ce13db828e9bf6b35e1 /src/main.c
parentcontrol: video frame capture + deterministic input-movie replay (diff)
downloadsl0pboy-24a015ff047d4b7bb955ba4bc61d5793d7492878.tar.gz
sl0pboy-24a015ff047d4b7bb955ba4bc61d5793d7492878.tar.xz
sl0pboy-24a015ff047d4b7bb955ba4bc61d5793d7492878.zip
serial: headless mode tying serial port to stdio for OS debugging
- --headless: no rendering/TUI; serial TX -> stdout, RX <- stdin (nonblocking) - generalize serial: serial_out_fd/serial_in_fd on the GB; internal-clock transfer now shifts in a real RX byte (from stdin) instead of always 0xFF - paced to native speed by default, --uncapped for turbo; pipes/scripts cleanly - README: document headless serial console
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c39
1 files changed, 38 insertions, 1 deletions
diff --git a/src/main.c b/src/main.c
index ecd818e..f0709c2 100644
--- a/src/main.c
+++ b/src/main.c
@@ -7,6 +7,8 @@
#include <string.h>
#include <time.h>
#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
static volatile sig_atomic_t running = 1;
static void on_sig(int s) { (void)s; running = 0; }
@@ -29,7 +31,7 @@ int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "usage: %s <rom> [--test seconds] [--sixel [scale]] "
"[--frameskip n] [--fps n] [--uncapped] [--fifo [path]] "
- "[--sock [path]]\n",
+ "[--sock [path]] [--headless]\n",
argv[0]);
return 1;
}
@@ -44,6 +46,7 @@ int main(int argc, char **argv) {
int frameskip = 0; // draw 1 of every (frameskip+1) frames
bool sixel = false; // use sixel graphics output
int sixel_scale = 2; // integer pixel zoom for sixel
+ bool headless = false; // no video; serial <-> stdio (OS debug console)
for (int i = 2; i < argc; i++) {
if (!strcmp(argv[i], "--test")) {
test = true;
@@ -66,6 +69,8 @@ int main(int argc, char **argv) {
if (i + 1 < argc && argv[i+1][0] != '-') sixel_scale = atoi(argv[++i]);
} else if (!strcmp(argv[i], "--uncapped") || !strcmp(argv[i], "--turbo")) {
target_fps = 0;
+ } else if (!strcmp(argv[i], "--headless")) {
+ headless = true;
}
}
if (frameskip < 0) frameskip = 0;
@@ -84,6 +89,38 @@ int main(int argc, char **argv) {
return 0;
}
+ // Headless: no rendering, no terminal UI. Tie the serial port to stdio so
+ // an OS on the GB gets a plain byte console (TX -> stdout, RX <- stdin).
+ // Paced to native speed unless --uncapped. Piping works for scripted tests:
+ // printf 'ls\n' | gbc rom.gb --headless
+ if (headless) {
+ gb->serial_out_fd = STDOUT_FILENO;
+ gb->serial_in_fd = STDIN_FILENO;
+ int fl = fcntl(STDIN_FILENO, F_GETFL, 0);
+ if (fl != -1) fcntl(STDIN_FILENO, F_SETFL, fl | O_NONBLOCK);
+ signal(SIGINT, on_sig);
+ signal(SIGTERM, on_sig);
+ bool uncapped = (target_fps <= 0);
+ double start_t = now_sec();
+ u64 start_cyc = gb->cycles;
+ const double HZ = 4194304.0;
+ while (running) {
+ run_frame(gb); // advance ~a frame's worth of cycles
+ if (!uncapped) {
+ double target = start_t + (gb->cycles - start_cyc) / HZ;
+ double sleep = target - now_sec();
+ if (sleep > 0) {
+ struct timespec ts = { (time_t)sleep,
+ (long)((sleep - (time_t)sleep) * 1e9) };
+ nanosleep(&ts, NULL);
+ } else if (sleep < -0.5) {
+ start_t = now_sec(); start_cyc = gb->cycles; // resync
+ }
+ }
+ }
+ cart_free(&gb->cart); free(gb); return 0;
+ }
+
if (shot_frames >= 0) {
for (int f = 0; f < shot_frames; f++) run_frame(gb);
FILE *pf = fopen(shot_path, "wb");