Async debugger commands

This commit is contained in:
Lior Halphon 2016-07-18 00:39:43 +03:00
parent 67f3a3a9d8
commit aa6438fa06
9 changed files with 120 additions and 42 deletions

View File

@ -13,6 +13,7 @@
unsigned long pendingLogLines; unsigned long pendingLogLines;
bool tooMuchLogs; bool tooMuchLogs;
bool fullScreen; bool fullScreen;
bool in_sync_input;
NSString *lastConsoleInput; NSString *lastConsoleInput;
} }
@ -21,6 +22,7 @@
- (void) vblank; - (void) vblank;
- (void) log: (const char *) log withAttributes: (GB_log_attributes) attributes; - (void) log: (const char *) log withAttributes: (GB_log_attributes) attributes;
- (const char *) getDebuggerInput; - (const char *) getDebuggerInput;
- (const char *) getAsyncDebuggerInput;
@end @end
static void vblank(GB_gameboy_t *gb) static void vblank(GB_gameboy_t *gb)
@ -41,6 +43,13 @@ static char *consoleInput(GB_gameboy_t *gb)
return strdup([self getDebuggerInput]); return strdup([self getDebuggerInput]);
} }
static char *asyncConsoleInput(GB_gameboy_t *gb)
{
Document *self = (__bridge Document *)(gb->user_data);
const char *ret = [self getAsyncDebuggerInput];
return ret? strdup(ret) : NULL;
}
static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
{ {
return (r << 0) | (g << 8) | (b << 16); return (r << 0) | (g << 8) | (b << 16);
@ -78,6 +87,7 @@ static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank); GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog); GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog);
GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput); GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput);
GB_set_async_input_callback(&gb, (GB_input_callback_t) asyncConsoleInput);
GB_set_rgb_encode_callback(&gb, rgbEncode); GB_set_rgb_encode_callback(&gb, rgbEncode);
gb.user_data = (__bridge void *)(self); gb.user_data = (__bridge void *)(self);
} }
@ -89,6 +99,7 @@ static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank); GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog); GB_set_log_callback(&gb, (GB_log_callback_t) consoleLog);
GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput); GB_set_input_callback(&gb, (GB_input_callback_t) consoleInput);
GB_set_async_input_callback(&gb, (GB_input_callback_t) asyncConsoleInput);
GB_set_rgb_encode_callback(&gb, rgbEncode); GB_set_rgb_encode_callback(&gb, rgbEncode);
gb.user_data = (__bridge void *)(self); gb.user_data = (__bridge void *)(self);
} }
@ -370,6 +381,10 @@ static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
else { else {
line = @""; line = @"";
} }
if (!in_sync_input) {
[self log:">"];
}
[self log:[line UTF8String]]; [self log:[line UTF8String]];
[self log:"\n"]; [self log:"\n"];
[has_debugger_input lock]; [has_debugger_input lock];
@ -382,10 +397,23 @@ static uint32_t rgbEncode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
- (const char *) getDebuggerInput - (const char *) getDebuggerInput
{ {
[self log:">"]; [self log:">"];
in_sync_input = true;
[has_debugger_input lockWhenCondition:1]; [has_debugger_input lockWhenCondition:1];
NSString *input = [debugger_input_queue firstObject]; NSString *input = [debugger_input_queue firstObject];
[debugger_input_queue removeObjectAtIndex:0]; [debugger_input_queue removeObjectAtIndex:0];
[has_debugger_input unlockWithCondition:[debugger_input_queue count] != 0]; [has_debugger_input unlockWithCondition:[debugger_input_queue count] != 0];
in_sync_input = false;
return [input UTF8String];
}
- (const char *) getAsyncDebuggerInput
{
[has_debugger_input lock];
NSString *input = [debugger_input_queue firstObject];
if (input) {
[debugger_input_queue removeObjectAtIndex:0];
}
[has_debugger_input unlockWithCondition:[debugger_input_queue count] != 0];
return [input UTF8String]; return [input UTF8String];
} }

View File

@ -605,18 +605,29 @@ static const char *lstrip(const char *str)
return str; return str;
} }
#define STOPPED_ONLY \
if (!gb->debug_stopped) { \
GB_log(gb, "Program is running. \n"); \
return false; \
}
static bool cont(GB_gameboy_t *gb, char *arguments) static bool cont(GB_gameboy_t *gb, char *arguments)
{ {
STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: continue\n"); GB_log(gb, "Usage: continue\n");
return true; return true;
} }
gb->debug_stopped = false; gb->debug_stopped = false;
return false; return false;
} }
static bool next(GB_gameboy_t *gb, char *arguments) static bool next(GB_gameboy_t *gb, char *arguments)
{ {
STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: next\n"); GB_log(gb, "Usage: next\n");
return true; return true;
@ -630,6 +641,8 @@ static bool next(GB_gameboy_t *gb, char *arguments)
static bool step(GB_gameboy_t *gb, char *arguments) static bool step(GB_gameboy_t *gb, char *arguments)
{ {
STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: step\n"); GB_log(gb, "Usage: step\n");
return true; return true;
@ -640,6 +653,8 @@ static bool step(GB_gameboy_t *gb, char *arguments)
static bool finish(GB_gameboy_t *gb, char *arguments) static bool finish(GB_gameboy_t *gb, char *arguments)
{ {
STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: finish\n"); GB_log(gb, "Usage: finish\n");
return true; return true;
@ -653,6 +668,8 @@ static bool finish(GB_gameboy_t *gb, char *arguments)
static bool stack_leak_detection(GB_gameboy_t *gb, char *arguments) static bool stack_leak_detection(GB_gameboy_t *gb, char *arguments)
{ {
STOPPED_ONLY
if (strlen(lstrip(arguments))) { if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: sld\n"); GB_log(gb, "Usage: sld\n");
return true; return true;
@ -1329,6 +1346,34 @@ void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr)
_GB_debugger_test_read_watchpoint(gb, full_addr); _GB_debugger_test_read_watchpoint(gb, full_addr);
} }
/* Returns true if debugger waits for more commands */
bool GB_debugger_do_command(GB_gameboy_t *gb, char *input)
{
if (!input[0]) {
return true;
}
char *command_string = input;
char *arguments = strchr(input, ' ');
if (arguments) {
/* Actually "split" the string. */
arguments[0] = 0;
arguments++;
}
else {
arguments = "";
}
const debugger_command_t *command = find_command(command_string);
if (command) {
return command->implementation(gb, arguments);
}
else {
GB_log(gb, "%s: no such command.\n", command_string);
return true;
}
}
void GB_debugger_run(GB_gameboy_t *gb) void GB_debugger_run(GB_gameboy_t *gb)
{ {
char *input = NULL; char *input = NULL;
@ -1355,34 +1400,22 @@ next_command:
gb->debug_fin_command = false; gb->debug_fin_command = false;
gb->stack_leak_detection = false; gb->stack_leak_detection = false;
input = gb->input_callback(gb); input = gb->input_callback(gb);
if (!input[0]) {
if (GB_debugger_do_command(gb, input)) {
goto next_command; goto next_command;
} }
char *command_string = input; free(input);
char *arguments = strchr(input, ' '); }
if (arguments) { }
/* Actually "split" the string. */
arguments[0] = 0;
arguments++;
}
else {
arguments = "";
}
const debugger_command_t *command = find_command(command_string); void GB_debugger_handle_async_commands(GB_gameboy_t *gb)
if (command) { {
if (command->implementation(gb, arguments)) { if (!gb->async_input_callback) return;
goto next_command; char *input = NULL;
}
}
else {
GB_log(gb, "%s: no such command.\n", command_string);
goto next_command;
}
/* Split to arguments and command */
while ((input = gb->async_input_callback(gb))) {
GB_debugger_do_command(gb, input);
free(input); free(input);
} }
} }

View File

@ -3,6 +3,7 @@
#include "gb.h" #include "gb.h"
void GB_debugger_run(GB_gameboy_t *gb); void GB_debugger_run(GB_gameboy_t *gb);
void GB_debugger_handle_async_commands(GB_gameboy_t *gb);
void GB_debugger_call_hook(GB_gameboy_t *gb); void GB_debugger_call_hook(GB_gameboy_t *gb);
void GB_debugger_ret_hook(GB_gameboy_t *gb); void GB_debugger_ret_hook(GB_gameboy_t *gb);
void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value); void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value);

View File

@ -219,6 +219,8 @@ void display_vblank(GB_gameboy_t *gb)
gb->last_vblank = nanoseconds; gb->last_vblank = nanoseconds;
} }
} }
gb->vblank_just_occured = true;
} }
static inline uint8_t scale_channel(uint8_t x) static inline uint8_t scale_channel(uint8_t x)

View File

@ -430,6 +430,11 @@ void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback)
gb->input_callback = callback; gb->input_callback = callback;
} }
void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback)
{
gb->async_input_callback = callback;
}
void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback) void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback)
{ {
gb->rgb_encode_callback = callback; gb->rgb_encode_callback = callback;

View File

@ -339,6 +339,7 @@ typedef struct GB_gameboy_s {
void *user_data; void *user_data;
GB_log_callback_t log_callback; GB_log_callback_t log_callback;
GB_input_callback_t input_callback; GB_input_callback_t input_callback;
GB_input_callback_t async_input_callback;
GB_rgb_encode_callback_t rgb_encode_callback; GB_rgb_encode_callback_t rgb_encode_callback;
GB_vblank_callback_t vblank_callback; GB_vblank_callback_t vblank_callback;
@ -368,6 +369,7 @@ typedef struct GB_gameboy_s {
bool turbo; bool turbo;
uint32_t ram_size; // Different between CGB and DMG uint32_t ram_size; // Different between CGB and DMG
uint8_t boot_rom[0x900]; uint8_t boot_rom[0x900];
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
} GB_gameboy_t; } GB_gameboy_t;
@ -393,6 +395,7 @@ void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback);
void GB_log(GB_gameboy_t *gb, const char *fmt, ...) __printflike(2, 3); void GB_log(GB_gameboy_t *gb, const char *fmt, ...) __printflike(2, 3);
void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) __printflike(3, 4); void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) __printflike(3, 4);
void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback); void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback);
void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback);
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate); void GB_set_sample_rate(GB_gameboy_t *gb, unsigned int sample_rate);
void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback); void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback);

View File

@ -50,31 +50,30 @@ void GB_timers_run(GB_gameboy_t *gb)
} }
} }
} }
}
/* RTC */ void GB_rtc_run(GB_gameboy_t *gb)
if (gb->display_cycles >= LCDC_PERIOD) { /* Time is a syscall and therefore is slow, so we update the RTC {
only during vblanks. */ if ((gb->rtc_high & 0x40) == 0) { /* is timer running? */
if ((gb->rtc_high & 0x40) == 0) { /* is timer running? */ time_t current_time = time(NULL);
time_t current_time = time(NULL); while (gb->last_rtc_second < current_time) {
while (gb->last_rtc_second < current_time) { gb->last_rtc_second++;
gb->last_rtc_second++; if (++gb->rtc_seconds == 60)
if (++gb->rtc_seconds == 60) {
gb->rtc_seconds = 0;
if (++gb->rtc_minutes == 60)
{ {
gb->rtc_seconds = 0; gb->rtc_minutes = 0;
if (++gb->rtc_minutes == 60) if (++gb->rtc_hours == 24)
{ {
gb->rtc_minutes = 0; gb->rtc_hours = 0;
if (++gb->rtc_hours == 24) if (++gb->rtc_days == 0)
{ {
gb->rtc_hours = 0; if (gb->rtc_high & 1) /* Bit 8 of days*/
if (++gb->rtc_days == 0)
{ {
if (gb->rtc_high & 1) /* Bit 8 of days*/ gb->rtc_high |= 0x80; /* Overflow bit */
{
gb->rtc_high |= 0x80; /* Overflow bit */
}
gb->rtc_high ^= 1;
} }
gb->rtc_high ^= 1;
} }
} }
} }

View File

@ -4,4 +4,5 @@
void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles); void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles);
void GB_timers_run(GB_gameboy_t *gb); void GB_timers_run(GB_gameboy_t *gb);
void GB_rtc_run(GB_gameboy_t *gb);
#endif /* timing_h */ #endif /* timing_h */

View File

@ -1309,6 +1309,7 @@ static GB_opcode_t *opcodes[256] = {
void GB_cpu_run(GB_gameboy_t *gb) void GB_cpu_run(GB_gameboy_t *gb)
{ {
gb->vblank_just_occured = false;
bool interrupt = gb->interrupt_enable & gb->io_registers[GB_IO_IF]; bool interrupt = gb->interrupt_enable & gb->io_registers[GB_IO_IF];
if (interrupt) { if (interrupt) {
gb->halted = false; gb->halted = false;
@ -1340,4 +1341,9 @@ void GB_cpu_run(GB_gameboy_t *gb)
else { else {
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
} }
if (gb->vblank_just_occured) {
GB_rtc_run(gb);
GB_debugger_handle_async_commands(gb);
}
} }