diff options
| author | gbc dev <gbc@localhost> | 2026-07-16 16:06:49 +0200 |
|---|---|---|
| committer | gbc dev <gbc@localhost> | 2026-07-16 16:06:49 +0200 |
| commit | 0b30297393430450486ce54ba5c3c47155a0dc68 (patch) | |
| tree | 2d978d65bc50e99a95e7e9c8a56d3cbdfb68d093 | |
| parent | main: --shot with --headless dumps final LCD on exit (diff) | |
| download | sl0pboy-0b30297393430450486ce54ba5c3c47155a0dc68.tar.gz sl0pboy-0b30297393430450486ce54ba5c3c47155a0dc68.tar.xz sl0pboy-0b30297393430450486ce54ba5c3c47155a0dc68.zip | |
main/gb: --keys button-script harness + serial_no_eof for OSK testing
--keys "udlrabse." plays scripted joypad taps (each char = one button
held 10 frames then released 10, so the guest sees a clean edge); pairs
with --headless --shot to drive on-screen input and capture the result.
When --keys is active, serial-RX EOF stays pending instead of sending
$04, so a shell blocked on read waits for OSK input instead of exiting.
| -rw-r--r-- | src/gb.c | 8 | ||||
| -rw-r--r-- | src/gb.h | 1 | ||||
| -rw-r--r-- | src/main.c | 25 |
3 files changed, 31 insertions, 3 deletions
@@ -140,9 +140,11 @@ void gb_write(GB *gb, u16 addr, u8 val) { gb->sc &= ~0x80; gb_request_interrupt(gb, INT_SERIAL); } else if (n == 0) { - gb->sb = 0x04; // EOF -> EOT (Ctrl-D) - gb->sc &= ~0x80; - gb_request_interrupt(gb, INT_SERIAL); + if (!gb->serial_no_eof) { + gb->sb = 0x04; // EOF -> EOT (Ctrl-D) + gb->sc &= ~0x80; + gb_request_interrupt(gb, INT_SERIAL); + } // else: stay pending (OSK provides input) } // n < 0 (EAGAIN on a live tty): stay pending, poll again } @@ -113,6 +113,7 @@ struct GB { bool hdma_active; u64 cycles; // total T-cycles elapsed + bool serial_no_eof; // if true, serial-RX EOF stays pending (don't send $04) bool serial_log; // print serial output to stderr (blargg tests) int serial_out_fd; // if >=0, transmitted serial bytes written here (stdout) int serial_in_fd; // if >=0, received serial bytes read here (stdin), else 0xFF @@ -28,6 +28,22 @@ static void run_frame(GB *gb) { cpu_step(gb); } +// Scripted button taps for testing: each char in `keys` = one token occupying +// 20 frames (pressed for the first 10, released for the next 10, so the guest +// sees a clean press+release edge). u/d/l/r=dpad, a/b, s=select, e=start, .=wait. +static u8 keys_mask(const char *keys, int frame) { + int tok = frame / 20; + if (tok >= (int)strlen(keys)) return 0; + if ((frame % 20) >= 10) return 0; + switch (keys[tok]) { + case 'u': return BTN_UP; case 'd': return BTN_DOWN; + case 'l': return BTN_LEFT; case 'r': return BTN_RIGHT; + case 'a': return BTN_A; case 'b': return BTN_B; + case 's': return BTN_SELECT; case 'e': return BTN_START; + default: return 0; + } +} + int main(int argc, char **argv) { if (argc < 2) { fprintf(stderr, "usage: %s <rom> [--test seconds] [--sixel [scale]] " @@ -48,6 +64,7 @@ int main(int argc, char **argv) { 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) + const char *keys = NULL; // scripted button taps for testing (u d l r a b s e .) for (int i = 2; i < argc; i++) { if (!strcmp(argv[i], "--test")) { test = true; @@ -55,6 +72,8 @@ int main(int argc, char **argv) { } 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]; + } else if (!strcmp(argv[i], "--keys")) { + if (i + 1 < argc) keys = argv[++i]; } else if (!strcmp(argv[i], "--fifo")) { fifo_path = (i + 1 < argc && argv[i+1][0] != '-') ? argv[++i] : "/tmp/gbc.fifo"; @@ -97,6 +116,8 @@ int main(int argc, char **argv) { if (headless) { gb->serial_out_fd = STDOUT_FILENO; gb->serial_in_fd = STDIN_FILENO; + gb->serial_no_eof = (keys != NULL); // OSK provides input; don't EOF-exit + int keys_frame = 0; int fl = fcntl(STDIN_FILENO, F_GETFL, 0); if (fl != -1) fcntl(STDIN_FILENO, F_SETFL, fl | O_NONBLOCK); signal(SIGINT, on_sig); @@ -121,6 +142,10 @@ int main(int argc, char **argv) { u64 start_cyc = gb->cycles; const double HZ = 4194304.0; while (running && !gb->poweroff) { + if (keys) { + gb->buttons = keys_mask(keys, keys_frame); + if (keys_frame++ > (int)strlen(keys) * 20 + 40) break; + } run_frame(gb); // advance ~a frame's worth of cycles if (!uncapped) { double target = start_t + (gb->cycles - start_cyc) / HZ; |
