#include "gb.h" #include "cpu.h" #include "render.h" #include #include #include #include #include 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) { fprintf(stderr, "usage: %s [--test seconds]\n", argv[0]); return 1; } 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) { free(gb); return 1; } gb_reset(gb); if (test) { gb->serial_log = true; u64 target = (u64)(seconds * 4194304.0); while (gb->cycles < target) cpu_step(gb); fprintf(stderr, "\n[ran %llu cycles]\n", (unsigned long long)gb->cycles); 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"); 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; }