diff options
Diffstat (limited to 'src/main.c')
| -rw-r--r-- | src/main.c | 39 |
1 files changed, 38 insertions, 1 deletions
@@ -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"); |
