aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.c
diff options
context:
space:
mode:
authorgbc dev <gbc@localhost>2026-07-14 23:26:49 +0200
committergbc dev <gbc@localhost>2026-07-14 23:26:49 +0200
commitc879adc43b91a53eb7e76f232a39abb56d8144c1 (patch)
tree3bd8cd77fed46ceb62504d2fb067f349bad2cf0e /src/main.c
parentspeed control: frame skipping to decouple terminal draw from emulation (diff)
downloadsl0pboy-c879adc43b91a53eb7e76f232a39abb56d8144c1.tar.gz
sl0pboy-c879adc43b91a53eb7e76f232a39abb56d8144c1.tar.xz
sl0pboy-c879adc43b91a53eb7e76f232a39abb56d8144c1.zip
control: socket-based debug channel + gbctl CLI driver
Replace the input-only FIFO's limitations with a Unix-domain socket control channel (--sock) that is a superset of the button vocabulary plus emulator introspection: read/write bus memory, get/set CPU context, single-step, PC breakpoints, value-change watchpoints, run/pause. Being a socket it replies to each command and broadcasts async 'event stop ...' lines; a stored last-stop record (stopinfo) lets per-request clients recover a missed event. Add gbctl: a stdlib-python CLI that spawns a tmux pane running the emulator on a ROM, auto-resolves the socket, and drives it with terse verbs (cpu/read/write/ reg/step/break/watch/continue/pause/press/hold/screen/stop). The FIFO stays as legacy input-only.
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c29
1 files changed, 27 insertions, 2 deletions
diff --git a/src/main.c b/src/main.c
index 5ce4fb5..541ce44 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,6 +1,7 @@
#include "gb.h"
#include "cpu.h"
#include "render.h"
+#include "control.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -26,7 +27,10 @@ static void run_frame(GB *gb) {
int main(int argc, char **argv) {
if (argc < 2) {
- fprintf(stderr, "usage: %s <rom> [--test seconds]\n", argv[0]);
+ fprintf(stderr, "usage: %s <rom> [--test seconds] [--sixel [scale]] "
+ "[--frameskip n] [--fps n] [--uncapped] [--fifo [path]] "
+ "[--sock [path]]\n",
+ argv[0]);
return 1;
}
const char *rom = argv[1];
@@ -35,8 +39,11 @@ int main(int argc, char **argv) {
int shot_frames = -1;
const char *shot_path = "shot.ppm";
const char *fifo_path = NULL;
+ const char *sock_path = NULL;
double target_fps = 59.73; // emulation speed cap; 0 = uncapped
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
for (int i = 2; i < argc; i++) {
if (!strcmp(argv[i], "--test")) {
test = true;
@@ -47,10 +54,16 @@ int main(int argc, char **argv) {
} else if (!strcmp(argv[i], "--fifo")) {
fifo_path = (i + 1 < argc && argv[i+1][0] != '-')
? argv[++i] : "/tmp/gbc.fifo";
+ } else if (!strcmp(argv[i], "--sock") || !strcmp(argv[i], "--socket")) {
+ sock_path = (i + 1 < argc && argv[i+1][0] != '-')
+ ? argv[++i] : "/tmp/gbc.sock";
} else if (!strcmp(argv[i], "--fps")) {
if (i + 1 < argc) target_fps = atof(argv[++i]);
} else if (!strcmp(argv[i], "--frameskip")) {
if (i + 1 < argc) frameskip = atoi(argv[++i]);
+ } else if (!strcmp(argv[i], "--sixel")) {
+ sixel = true;
+ 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;
}
@@ -90,6 +103,14 @@ int main(int argc, char **argv) {
fprintf(stderr, "control fifo: echo 'a' > %s (a b start select "
"up down left right; +x/-x hold/release; release)\n", fifo_path);
}
+ if (sock_path) {
+ control_open(sock_path);
+ if (control_active())
+ fprintf(stderr, "control socket: %s (buttons + debug: cpu read "
+ "write step break watch continue pause; 'help')\n",
+ sock_path);
+ }
+ if (sixel) render_set_sixel(true, sixel_scale);
term_init();
// The emulator always advances every GB frame at the paced native rate so
@@ -107,13 +128,16 @@ int main(int argc, char **argv) {
while (running) {
frames++;
if (!input_poll(gb)) break;
+ if (!control_poll(gb)) break; // socket may inject input / debug cmds
if (input_take_turbo()) turbo = !turbo;
frameskip += input_take_frameskip_delta();
if (frameskip < 0) frameskip = 0;
if (frameskip > 30) frameskip = 30;
render_set_hud(frameskip, turbo);
- run_frame(gb); // always emulate a full frame
+ // Emulate a full frame, honoring debugger pause/step/breakpoints when
+ // the control socket is active (otherwise a plain full frame).
+ control_run_frame(gb);
// decide whether to draw this frame
bool do_render = (since_render >= frameskip);
@@ -148,6 +172,7 @@ int main(int argc, char **argv) {
term_restore();
input_close_fifo();
+ control_close();
double elapsed = now_sec() - start_time;
if (elapsed > 0)
fprintf(stderr, "emulated %llu frames (%.1f fps), drew %llu (%.1f fps) "