cpu: get blarg CPU instruction test 6 passing
Its been too long since a checkin, but here's some of the improvements: - Support for diffing with other emulators - Better disassmbed output - New CPU instructions implemented - Lots of CPU fixes
This commit is contained in:
133
src/apps/gbdb.c
133
src/apps/gbdb.c
@@ -29,6 +29,7 @@ typedef void (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 usage[] =
|
||||
@@ -38,7 +39,7 @@ static const char usage[] =
|
||||
"break <addr>: Adds a breakpoint for the given addess\n"
|
||||
"step <cycles>: executes \"cycle\" instructions (default 1)\n"
|
||||
"regs: dumps the state of the registers\n"
|
||||
"peek: view the next instruction to run\n"
|
||||
"disas: view the next instruction to run\n"
|
||||
"exit: quit the program\n";
|
||||
|
||||
static struct lr35902_ops cpu_ops;
|
||||
@@ -53,6 +54,7 @@ static volatile sig_atomic_t paused = 0;
|
||||
static struct {
|
||||
bool quiet;
|
||||
int log_fd;
|
||||
int trace_fd;
|
||||
} config;
|
||||
|
||||
void gb_log(const char *fmt, ...)
|
||||
@@ -112,12 +114,34 @@ static void init(void)
|
||||
gb_mem_init(memory, &video);
|
||||
|
||||
config.log_fd = -1;
|
||||
config.trace_fd = -1;
|
||||
}
|
||||
|
||||
static void print_trace(void)
|
||||
{
|
||||
if (config.trace_fd > -1) {
|
||||
uint8_t instr = gb_mem_read(memory, cpu.pc);
|
||||
|
||||
dprintf(config.trace_fd,
|
||||
"A:%02X F:%c%c%c%c BC:%04X DE:%04x HL:%04x SP:%04x PC:%04x 0x%02x",
|
||||
cpu.a,
|
||||
(cpu.zf) ? 'Z' : '-', (cpu.nf) ? 'N' : '-',
|
||||
(cpu.hf) ? 'H' : '-', (cpu.cf) ? 'C' : '-',
|
||||
cpu.bc, cpu.de, cpu.hl, cpu.sp, cpu.pc, instr);
|
||||
|
||||
if (instr == 0xcb) {
|
||||
dprintf(config.trace_fd, " 0x%02x", gb_mem_read(memory, cpu.pc + 1));
|
||||
}
|
||||
|
||||
dprintf(config.trace_fd, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void cycle()
|
||||
{
|
||||
int cycles;
|
||||
|
||||
print_trace();
|
||||
cycles = lr35902_cycle(&cpu);
|
||||
gb_video_cycle(&video, cycles);
|
||||
}
|
||||
@@ -159,9 +183,6 @@ static void set_logfile(struct tokens **tokens)
|
||||
char *token = token_next(tokens);
|
||||
int fd;
|
||||
|
||||
if (token != NULL) {
|
||||
strip_newline(token);
|
||||
}
|
||||
|
||||
if (token == NULL || strcmp(token, "") == 0) {
|
||||
if (config.log_fd != -1) {
|
||||
@@ -186,6 +207,36 @@ static void set_logfile(struct tokens **tokens)
|
||||
config.log_fd = fd;
|
||||
}
|
||||
|
||||
static void set_tracefile(struct tokens **tokens)
|
||||
{
|
||||
char *token = token_next(tokens);
|
||||
int fd;
|
||||
|
||||
|
||||
if (token == NULL || strcmp(token, "") == 0) {
|
||||
if (config.trace_fd >= -1) {
|
||||
close(config.trace_fd);
|
||||
}
|
||||
config.trace_fd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
fd = open(token,
|
||||
O_WRONLY | O_TRUNC | O_CREAT,
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (fd < 0) {
|
||||
gb_error("Failed to open tracefile %s, %s\n", token, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.trace_fd != -1) {
|
||||
close(config.trace_fd);
|
||||
}
|
||||
|
||||
config.trace_fd = fd;
|
||||
gb_log("Trace fd=%d\n", config.trace_fd);
|
||||
}
|
||||
|
||||
static void set_quiet_on(struct tokens **tokens)
|
||||
{
|
||||
config.quiet = true;
|
||||
@@ -227,6 +278,7 @@ static void set(struct tokens **tokens)
|
||||
tri_init(&commands);
|
||||
tri_add_string(&commands, "quiet", set_quiet);
|
||||
tri_add_string(&commands, "logfile", set_logfile);
|
||||
tri_add_string(&commands, "trace", set_tracefile);
|
||||
tri_add_string(&commands, "video_debug", set_video_debug);
|
||||
init = true;
|
||||
}
|
||||
@@ -259,11 +311,11 @@ static void step(struct tokens **tokens)
|
||||
{
|
||||
uint64_t steps, end_steps;
|
||||
char *token;
|
||||
|
||||
token = token_next(tokens);
|
||||
if (token == NULL) {
|
||||
steps = 1;
|
||||
} else {
|
||||
strip_newline(token);
|
||||
steps = strtol(token, NULL, 0);
|
||||
}
|
||||
|
||||
@@ -305,16 +357,42 @@ static void regs(struct tokens **tokens)
|
||||
gb_log("SP: 0x%04x\n", cpu.sp);
|
||||
}
|
||||
|
||||
static void peek(struct tokens **tokens)
|
||||
static uint8_t decode_fetch_mem(void *opaque, uint16_t addr)
|
||||
{
|
||||
return gb_mem_read(memory, addr);
|
||||
}
|
||||
|
||||
static void disas(struct tokens **tokens)
|
||||
{
|
||||
uint16_t pc = lr35902_get_reg_16(&cpu, LR35902_REG_PC);
|
||||
uint8_t byte = gb_mem_read(memory, pc);
|
||||
gb_log("0x%04x:%s\n", pc, gb_byte_to_opcode(byte));
|
||||
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) {
|
||||
num_disas = 1;
|
||||
} else {
|
||||
num_disas = parse_val(disas_cnt_str);
|
||||
}
|
||||
} else {
|
||||
num_disas = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_disas; i++) {
|
||||
const char *opcode;
|
||||
old_pc = pc;
|
||||
opcode = gb_byte_to_opcode(&pc, decode_fetch_mem, &memory);
|
||||
gb_log("0x%04x:%s\n", old_pc, opcode);
|
||||
free((void *) opcode);
|
||||
}
|
||||
}
|
||||
|
||||
static void vstep(struct tokens **tokens)
|
||||
{
|
||||
peek(NULL);
|
||||
disas(NULL);
|
||||
step(tokens);
|
||||
regs(NULL);
|
||||
}
|
||||
@@ -359,7 +437,7 @@ static int64_t parse_reg_str(const char *str)
|
||||
{ "c", cpu.c },
|
||||
{ "d", cpu.d },
|
||||
{ "e", cpu.e },
|
||||
{ "f", (cpu.zf << 3) | (cpu.nf << 2) | (cpu.hf << 1) | (cpu.cf << 0) },
|
||||
{ "f", f },
|
||||
};
|
||||
|
||||
|
||||
@@ -387,7 +465,7 @@ static void assert(struct tokens **tokens)
|
||||
const char *usage = "usage: assert <value> <operator> <value>\n";
|
||||
int64_t val0, val1;
|
||||
|
||||
val0_str = token_next(tokens);
|
||||
val0_str = strdup(token_next(tokens));
|
||||
if (val0_str == NULL) {
|
||||
gb_error("%s", usage);
|
||||
return;
|
||||
@@ -400,7 +478,7 @@ static void assert(struct tokens **tokens)
|
||||
return;
|
||||
}
|
||||
|
||||
val1_str = token_next(tokens);
|
||||
val1_str = strdup(token_next(tokens));
|
||||
if (val1_str == NULL) {
|
||||
gb_error("%s", usage);
|
||||
return;
|
||||
@@ -438,7 +516,7 @@ static void assert(struct tokens **tokens)
|
||||
return;
|
||||
fail:
|
||||
gb_error("ASSERT: %s %s %s\n", val0_str, operator_str, val1_str);
|
||||
gb_error("%s=%" PRId64 ", %s=%" PRId64 "\n", val0_str, val0, val1_str, val1);
|
||||
gb_error("%s=0x%" PRIx64 ", %s=0x%" PRIx64 "\n", val0_str, val0, val1_str, val1);
|
||||
free(val0_str);
|
||||
free(val1_str);
|
||||
free(operator_str);
|
||||
@@ -449,10 +527,9 @@ fail:
|
||||
|
||||
static void mem(struct tokens **tokens)
|
||||
{
|
||||
uint16_t addr, bytes, i;
|
||||
uint16_t addr, bytes, addr_end;
|
||||
char *token = token_next(tokens);
|
||||
|
||||
gb_log("mem_addr=%s\n", token);
|
||||
int i;
|
||||
|
||||
if (token == NULL) {
|
||||
gb_error("usage: mem <addr> (num bytes) (format)\n");
|
||||
@@ -461,29 +538,16 @@ static void mem(struct tokens **tokens)
|
||||
|
||||
addr = parse_val(token);
|
||||
token = token_next(tokens);
|
||||
gb_log("mem_bytes=%s\n", token);
|
||||
if (token == NULL) {
|
||||
bytes = 1;
|
||||
} else {
|
||||
bytes = parse_val(token);
|
||||
}
|
||||
|
||||
token = token_next(tokens);
|
||||
gb_log("mem_format=%s\n", token);
|
||||
|
||||
for (i = 0; i < bytes; i++) {
|
||||
//TODO: Make sure this has no side effects
|
||||
uint8_t val = gb_mem_read(memory, addr + i);
|
||||
|
||||
if (token != NULL && token[0] == 'i') {
|
||||
gb_log("0x%04x:%s\n", addr + i, gb_byte_to_opcode(val));
|
||||
} else if (token != NULL && token[0] == 'b') {
|
||||
gb_log("0x%04x:0x%02x %s\n", addr + i, val, gb_byte_to_opcode(val));
|
||||
} else {
|
||||
gb_log("0x%04x:0x%02x\n", addr + i, val);
|
||||
}
|
||||
gb_log("0x%04x: 0x%02x\n", addr + i, gb_mem_read(memory, addr + i));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void load(struct tokens **tokens)
|
||||
@@ -749,6 +813,9 @@ static void process_cmd(const char *cmd_str, struct tri *commands)
|
||||
}
|
||||
|
||||
call_next_cmd(&tokens, commands, "");
|
||||
|
||||
/* Ensure there are no leftover tokens to leak */
|
||||
do {} while (token_next(&tokens));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -795,7 +862,7 @@ int main(int argc, char **argv)
|
||||
tri_add_string(&commands, "mem", mem);
|
||||
/* FIXME */
|
||||
/* tri_add_string(&commands, "dump", mem_dump); */
|
||||
tri_add_string(&commands, "peek", peek);
|
||||
tri_add_string(&commands, "disassemble", disas);
|
||||
tri_add_string(&commands, "help", help);
|
||||
tri_add_string(&commands, "?", help);
|
||||
tri_add_string(&commands, "breakpoint", breakpoint);
|
||||
@@ -839,5 +906,9 @@ int main(int argc, char **argv)
|
||||
close(config.log_fd);
|
||||
}
|
||||
|
||||
if (config.trace_fd != -1) {
|
||||
close(config.log_fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user