diff options
| author | gbc dev <gbc@localhost> | 2026-07-04 22:12:57 +0200 |
|---|---|---|
| committer | gbc dev <gbc@localhost> | 2026-07-04 22:12:57 +0200 |
| commit | 5800f04eb0417c980fef2eddd78d8b60d19d0db5 (patch) | |
| tree | 82f29e7b97b6b18eb077dcac51b6407c5966ba0b /src/main.c | |
| parent | core emulator: SM83 CPU, MMU, timer, PPU, CGB support (diff) | |
| download | sl0pboy-5800f04eb0417c980fef2eddd78d8b60d19d0db5.tar.gz sl0pboy-5800f04eb0417c980fef2eddd78d8b60d19d0db5.tar.xz sl0pboy-5800f04eb0417c980fef2eddd78d8b60d19d0db5.zip | |
terminal renderer (truecolor half-block), input, screenshot mode
Diffstat (limited to 'src/main.c')
| -rw-r--r-- | src/main.c | 64 |
1 files changed, 61 insertions, 3 deletions
@@ -1,8 +1,28 @@ #include "gb.h" #include "cpu.h" +#include "render.h" #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <time.h> +#include <signal.h> + +static volatile sig_atomic_t running = 1; +static void on_sig(int s) { (void)s; running = 0; } + +static double now_sec(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec + ts.tv_nsec / 1e9; +} + +static void run_frame(GB *gb) { + gb->ppu.frame_ready = false; + // safety bound in case LCD is off (no frame_ready) + u64 budget = gb->cycles + 70224 * 2; + while (!gb->ppu.frame_ready && gb->cycles < budget) + cpu_step(gb); +} int main(int argc, char **argv) { if (argc < 2) { @@ -12,15 +32,20 @@ int main(int argc, char **argv) { const char *rom = argv[1]; bool test = false; double seconds = 30.0; + int shot_frames = -1; + const char *shot_path = "shot.ppm"; for (int i = 2; i < argc; i++) { if (!strcmp(argv[i], "--test")) { test = true; if (i + 1 < argc) seconds = atof(argv[++i]); + } else if (!strcmp(argv[i], "--shot")) { + if (i + 1 < argc) shot_frames = atoi(argv[++i]); + if (i + 1 < argc && argv[i+1][0] != '-') shot_path = argv[++i]; } } GB *gb = calloc(1, sizeof(GB)); - if (cart_load(&gb->cart, rom) != 0) return 1; + if (cart_load(&gb->cart, rom) != 0) { free(gb); return 1; } gb_reset(gb); if (test) { @@ -33,8 +58,41 @@ int main(int argc, char **argv) { return 0; } - // TODO: interactive terminal rendering - fprintf(stderr, "interactive mode not yet implemented; use --test\n"); + if (shot_frames >= 0) { + for (int f = 0; f < shot_frames; f++) run_frame(gb); + FILE *pf = fopen(shot_path, "wb"); + fprintf(pf, "P6\n%d %d\n255\n", SCREEN_W, SCREEN_H); + for (int y = 0; y < SCREEN_H; y++) + for (int x = 0; x < SCREEN_W; x++) + fwrite(gb->ppu.fb[y][x], 1, 3, pf); + fclose(pf); + fprintf(stderr, "wrote %s after %d frames\n", shot_path, shot_frames); + cart_free(&gb->cart); free(gb); return 0; + } + + signal(SIGINT, on_sig); + signal(SIGTERM, on_sig); + term_init(); + + const double frame_time = 1.0 / 59.73; + double next = now_sec(); + while (running) { + if (!input_poll(gb)) break; + run_frame(gb); + render_frame(gb); + + next += frame_time; + double t = now_sec(); + double sleep = next - t; + if (sleep > 0) { + struct timespec ts = { (time_t)sleep, (long)((sleep - (time_t)sleep) * 1e9) }; + nanosleep(&ts, NULL); + } else if (sleep < -0.1) { + next = t; // fell behind; resync + } + } + + term_restore(); cart_free(&gb->cart); free(gb); return 0; |
