integrate readline with gbdb

...for better interactive debugging with history.

Also, make commands return error codes so that only commands that
execute successfully will be added to the command history.
This commit is contained in:
2018-09-08 21:33:30 -07:00
parent b22fc6f44a
commit 5675aedc91
2 changed files with 90 additions and 68 deletions

View File

@@ -11,7 +11,7 @@ CCFLAGS += -std=c11 -D_POSIX_C_SOURCE=200809L
CCFLAGS += -I$(SRC_DIR)
CCFLAGS += -I/usr/include/glib-2.0/ -I/usr/lib/x86_64-linux-gnu/glib-2.0/include
LDFLAGS += -lglib-2.0
LDFLAGS += -lglib-2.0 -lreadline
CCFLAGS += $(CCFLAGS_EXTRA)
LDFLAGS += $(LDFLAGS_EXTRA)

View File

@@ -15,6 +15,9 @@
#include <fcntl.h>
#include <unistd.h>
#include <readline/readline.h>
#include <readline/history.h>
#include "common/common.h"
#include "gb_disas.h"
#include "gbemu/cpu.h"
@@ -24,14 +27,14 @@
#define INPUT_MAX_LEN 512
#define MAX_BREAKPTS 16 /* Should be plenty for anyone */
typedef void (gbdb_cmd)(struct tokens **tokens);
typedef int (gbdb_cmd)(struct tokens **tokens);
static void breakpoint_addr_hit(uint16_t id);
static void breakpoint_cb(struct lr35902_state *lr);
static void reset_breakpoints(void);
static int64_t parse_val(const char *str);
static const char prompt[] = "gbdb >";
static const char prompt[] = "gbdb > ";
static const char usage[] =
"Available commands:\n"
"load <file>: loads the given file as the gameboy cartridge\n"
@@ -146,13 +149,7 @@ static void cycle()
gb_video_cycle(&video, cycles);
}
static void show_prompt()
{
printf("%s ", prompt);
fflush(stdout);
}
static void call_next_cmd(struct tokens** tokens,
static int call_next_cmd(struct tokens** tokens,
struct tri *commands,
const char *cmdname)
{
@@ -162,23 +159,23 @@ static void call_next_cmd(struct tokens** tokens,
char *token = token_next(tokens);
if (!token) {
return;
return -1;
}
cmd = tri_get_string_autocomplete(commands, token, &ambiguous);
if (ambiguous) {
gb_error("ambiguous %s command: '%s'\n", cmdname, token);
return;
return -1;
}
if (cmd == NULL) {
gb_error("unrecognized %s command: '%s'\n", cmdname, token);
return;
return -1;
}
cmd(tokens);
return cmd(tokens);
}
static void set_logfile(struct tokens **tokens)
static int set_logfile(struct tokens **tokens)
{
char *token = token_next(tokens);
int fd;
@@ -189,7 +186,7 @@ static void set_logfile(struct tokens **tokens)
close(config.log_fd);
}
config.log_fd = -1;
return;
return -1;
}
fd = open(token,
@@ -197,7 +194,7 @@ static void set_logfile(struct tokens **tokens)
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
gb_error("Failed to open logfile %s, %s\n", token, strerror(errno));
return;
return -1;
}
if (config.log_fd != -1) {
@@ -205,9 +202,11 @@ static void set_logfile(struct tokens **tokens)
}
config.log_fd = fd;
return 0;
}
static void set_tracefile(struct tokens **tokens)
static int set_tracefile(struct tokens **tokens)
{
char *token = token_next(tokens);
int fd;
@@ -218,7 +217,7 @@ static void set_tracefile(struct tokens **tokens)
close(config.trace_fd);
}
config.trace_fd = -1;
return;
return -1;
}
fd = open(token,
@@ -226,7 +225,7 @@ static void set_tracefile(struct tokens **tokens)
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
gb_error("Failed to open tracefile %s, %s\n", token, strerror(errno));
return;
return -1;
}
if (config.trace_fd != -1) {
@@ -235,19 +234,22 @@ static void set_tracefile(struct tokens **tokens)
config.trace_fd = fd;
gb_log("Trace fd=%d\n", config.trace_fd);
return 0;
}
static void set_quiet_on(struct tokens **tokens)
static int set_quiet_on(struct tokens **tokens)
{
config.quiet = true;
return 0;
}
static void set_quiet_off(struct tokens **tokens)
static int set_quiet_off(struct tokens **tokens)
{
config.quiet = false;
return 0;
}
static void set_quiet(struct tokens **tokens)
static int set_quiet(struct tokens **tokens)
{
static bool init = false;
static struct tri commands;
@@ -261,15 +263,16 @@ static void set_quiet(struct tokens **tokens)
init = true;
}
call_next_cmd(tokens, &commands, "set quiet");
return call_next_cmd(tokens, &commands, "set quiet");
}
static void set_video_debug(struct tokens **tokens)
static int set_video_debug(struct tokens **tokens)
{
video.debug_logging = true;
return 0;
}
static void set(struct tokens **tokens)
static int set(struct tokens **tokens)
{
static bool init = false;
static struct tri commands;
@@ -283,10 +286,10 @@ static void set(struct tokens **tokens)
init = true;
}
call_next_cmd(tokens, &commands, "set");
return call_next_cmd(tokens, &commands, "set");
}
static void echo(struct tokens **tokens)
static int echo(struct tokens **tokens)
{
char *token;
while ((token = token_next(tokens)) != NULL) {
@@ -294,6 +297,7 @@ static void echo(struct tokens **tokens)
}
gb_log("\n");
return 0;
}
static void breakpoint_cb(struct lr35902_state *lr)
@@ -307,7 +311,7 @@ static void breakpoint_cb(struct lr35902_state *lr)
}
static void step(struct tokens **tokens)
static int step(struct tokens **tokens)
{
uint64_t steps, end_steps;
char *token;
@@ -342,9 +346,11 @@ static void step(struct tokens **tokens)
paused = 0;
paused_signal = 0;
paused_breakpoint = 0;
return 0;
}
static void regs(struct tokens **tokens)
static int regs(struct tokens **tokens)
{
uint8_t f = (cpu.nf << CPU_F_BIT_POS_N) | (cpu.zf << CPU_F_BIT_POS_Z) |
(cpu.cf << CPU_F_BIT_POS_C) | (cpu.hf << CPU_F_BIT_POS_H);
@@ -355,6 +361,8 @@ static void regs(struct tokens **tokens)
gb_log("HL: 0x%04x H: 0x%02x L: 0x%02x\n", cpu.hl, cpu.h, cpu.l);
gb_log("PC: 0x%04x\n", cpu.pc);
gb_log("SP: 0x%04x\n", cpu.sp);
return 0;
}
static uint8_t decode_fetch_mem(void *opaque, uint16_t addr)
@@ -362,14 +370,13 @@ static uint8_t decode_fetch_mem(void *opaque, uint16_t addr)
return gb_mem_read(memory, addr);
}
static void disas(struct tokens **tokens)
static int disas(struct tokens **tokens)
{
uint16_t pc = lr35902_get_reg_16(&cpu, LR35902_REG_PC);
uint16_t old_pc = pc;
const char *disas_cnt_str;
int64_t num_disas, i;
if (tokens) {
disas_cnt_str = token_next(tokens);
if (disas_cnt_str == NULL) {
@@ -388,32 +395,45 @@ static void disas(struct tokens **tokens)
gb_log("0x%04x:%s\n", old_pc, opcode);
free((void *) opcode);
}
return 0;
}
static void vstep(struct tokens **tokens)
static int vstep(struct tokens **tokens)
{
int ret;
disas(NULL);
step(tokens);
ret = step(tokens);
regs(NULL);
return ret;
}
static void stats(struct tokens **tokens)
static int stats(struct tokens **tokens)
{
gb_log("Cycles: %" PRId64 "\n", cpu.metrics.cycles);
gb_log("Retired Insructions %" PRId64 "\n", cpu.metrics.retired_instrs);
gb_log("Memory Reads: %" PRId64 " \n", cpu.metrics.mem_reads);
gb_log("Memory Writes: %" PRId64 "\n", cpu.metrics.mem_writes);
return 0;
}
static void comment(struct tokens **tokens)
static int comment(struct tokens **tokens)
{
/* Gobble up everything */
do {} while (token_next(tokens));
// Return an error so it does not get stored in the history
return -1;
}
static void help(struct tokens **tokens)
static int help(struct tokens **tokens)
{
gb_log(usage);
return 0;
}
static int64_t parse_reg_str(const char *str)
@@ -602,7 +622,7 @@ static __attribute__((hot)) void do_run(void) {
paused_signal = 0;
}
static void run(struct tokens **tokens )
static void run(struct tokens **tokens)
{
do_run();
}
@@ -652,41 +672,45 @@ static int do_set_breakpoint(uint16_t addr, bool temp)
num_breakpoints++;
} else {
gb_error("maximum number of breakpoints reached\n");
return -1;
}
return 0;
}
static void set_breakpoint(struct tokens **tokens)
static int set_breakpoint(struct tokens **tokens)
{
uint16_t addr, i;
char *token = token_next(tokens);
if (token == NULL) {
gb_error("usage: breakpoint add <addr>\n");
return;
return -1;
}
addr = parse_val(token);
do_set_breakpoint(addr, false);
return do_set_breakpoint(addr, false);
}
static void runto(struct tokens **tokens)
static int runto(struct tokens **tokens)
{
uint16_t addr, i, rc;
char *token = token_next(tokens);
if (token == NULL) {
gb_error("usage: runto <addr>\n");
return;
return -1;
}
addr = parse_val(token);
rc = do_set_breakpoint(addr, true);
if (rc < 0) {
gb_error("failed to set breakpoint\n");
return;
return -1;
}
do_run();
return 0;
}
static int do_delete_breakpoint(int id)
@@ -729,24 +753,28 @@ static void breakpoint_addr_hit(uint16_t addr)
if (bkpt->temp) {
do_delete_breakpoint(bkpt->id);
}
return;
}
static void delete_breakpoint(struct tokens **tokens)
static int delete_breakpoint(struct tokens **tokens)
{
char *token = token_next(tokens);
int rc, bkpt;
int ret, bkpt;
if (token == NULL) {
gb_error("usage: breakpoint rm <bkpt num>\n");
return;
return -1;
}
bkpt = parse_val(token);
rc = do_delete_breakpoint(bkpt);
if (rc < 0) {
ret = do_delete_breakpoint(bkpt);
if (ret < 0) {
gb_error("%d is not a valid breakpoint number\n", bkpt);
}
return ret;
}
static void display_breakpoints(struct tokens **tokens)
@@ -762,7 +790,7 @@ static void display_breakpoints(struct tokens **tokens)
}
}
static void breakpoint(struct tokens **tokens)
static int breakpoint(struct tokens **tokens)
{
static bool init = false;
static struct tri commands;
@@ -780,7 +808,7 @@ static void breakpoint(struct tokens **tokens)
init = true;
}
call_next_cmd(tokens, &commands, "breakpoint");
return call_next_cmd(tokens, &commands, "breakpoint");
}
static bool breakpoint_is_at_addr(uint16_t addr) {
@@ -795,15 +823,16 @@ static bool breakpoint_is_at_addr(uint16_t addr) {
return false;
}
static void process_cmd(const char *cmd_str, struct tri *commands)
static int process_cmd(const char *cmd_str, struct tri *commands)
{
struct tokens *statements;
struct tokens *tokens;
char *statement, *token;
int ret = -1;
statements = tokenize(cmd_str, ";\n");
if (!statements) {
return;
return -1;
}
while ((statement = token_next(&statements)) != NULL) {
@@ -812,12 +841,13 @@ static void process_cmd(const char *cmd_str, struct tri *commands)
continue;
}
call_next_cmd(&tokens, commands, "");
ret = call_next_cmd(&tokens, commands, "");
/* Ensure there are no leftover tokens to leak */
do {} while (token_next(&tokens));
}
return ret;
}
static int load_initfile(const char *initfile_path, struct tri *commands)
@@ -842,11 +872,6 @@ static int load_initfile(const char *initfile_path, struct tri *commands)
int main(int argc, char **argv)
{
struct tri commands;
char line_buffer[INPUT_MAX_LEN]; /* Buffer for input command */
char old_buffer[INPUT_MAX_LEN]; /* Buffer for previous input */
line_buffer[0] = '\0';
old_buffer[0] = '\0';
tri_init(&commands);
tri_add_string(&commands, "#", comment);
@@ -883,23 +908,20 @@ int main(int argc, char **argv)
while (1) {
/* String to use for input (line_buffer or
* old_buffer) */
char *cmd_str;
char *cmd_str = readline(prompt);
int ret;
show_prompt();
cmd_str = fgets(line_buffer, INPUT_MAX_LEN, stdin);
if (cmd_str == NULL) {
printf("\n");
return 0;
}
if (line_buffer[0] != '\n') {
cmd_str = line_buffer;
strncpy(old_buffer, line_buffer, INPUT_MAX_LEN);
} else {
cmd_str = old_buffer;
ret = process_cmd(cmd_str, &commands);
if (ret == 0) {
add_history(cmd_str);
}
process_cmd(cmd_str, &commands);
free(cmd_str);
}
if (config.log_fd != -1) {