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;
bool tooMuchLogs;
bool fullScreen;
bool in_sync_input;
NSString *lastConsoleInput;
}
@ -21,6 +22,7 @@
- (void) vblank;
- (void) log: (const char *) log withAttributes: (GB_log_attributes) attributes;
- (const char *) getDebuggerInput;
- (const char *) getAsyncDebuggerInput;
@end
static void vblank(GB_gameboy_t *gb)
@ -41,6 +43,13 @@ static char *consoleInput(GB_gameboy_t *gb)
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)
{
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_log_callback(&gb, (GB_log_callback_t) consoleLog);
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.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_log_callback(&gb, (GB_log_callback_t) consoleLog);
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.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 {
line = @"";
}
if (!in_sync_input) {
[self log:">"];
}
[self log:[line UTF8String]];
[self log:"\n"];
[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
{
[self log:">"];
in_sync_input = true;
[has_debugger_input lockWhenCondition:1];
NSString *input = [debugger_input_queue firstObject];
[debugger_input_queue removeObjectAtIndex: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];
}

View File

@ -605,18 +605,29 @@ static const char *lstrip(const char *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)
{
STOPPED_ONLY
if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: continue\n");
return true;
}
gb->debug_stopped = false;
return false;
}
static bool next(GB_gameboy_t *gb, char *arguments)
{
STOPPED_ONLY
if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: next\n");
return true;
@ -630,6 +641,8 @@ static bool next(GB_gameboy_t *gb, char *arguments)
static bool step(GB_gameboy_t *gb, char *arguments)
{
STOPPED_ONLY
if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: step\n");
return true;
@ -640,6 +653,8 @@ static bool step(GB_gameboy_t *gb, char *arguments)
static bool finish(GB_gameboy_t *gb, char *arguments)
{
STOPPED_ONLY
if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: finish\n");
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)
{
STOPPED_ONLY
if (strlen(lstrip(arguments))) {
GB_log(gb, "Usage: sld\n");
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);
}
/* 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)
{
char *input = NULL;
@ -1355,34 +1400,22 @@ next_command:
gb->debug_fin_command = false;
gb->stack_leak_detection = false;
input = gb->input_callback(gb);
if (!input[0]) {
if (GB_debugger_do_command(gb, input)) {
goto next_command;
}
char *command_string = input;
char *arguments = strchr(input, ' ');
if (arguments) {
/* Actually "split" the string. */
arguments[0] = 0;
arguments++;
}
else {
arguments = "";
}
free(input);
}
}
const debugger_command_t *command = find_command(command_string);
if (command) {
if (command->implementation(gb, arguments)) {
goto next_command;
}
}
else {
GB_log(gb, "%s: no such command.\n", command_string);
goto next_command;
}
/* Split to arguments and command */
void GB_debugger_handle_async_commands(GB_gameboy_t *gb)
{
if (!gb->async_input_callback) return;
char *input = NULL;
while ((input = gb->async_input_callback(gb))) {
GB_debugger_do_command(gb, input);
free(input);
}
}

View File

@ -3,6 +3,7 @@
#include "gb.h"
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_ret_hook(GB_gameboy_t *gb);
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->vblank_just_occured = true;
}
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;
}
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)
{
gb->rgb_encode_callback = callback;

View File

@ -339,6 +339,7 @@ typedef struct GB_gameboy_s {
void *user_data;
GB_log_callback_t log_callback;
GB_input_callback_t input_callback;
GB_input_callback_t async_input_callback;
GB_rgb_encode_callback_t rgb_encode_callback;
GB_vblank_callback_t vblank_callback;
@ -368,6 +369,7 @@ typedef struct GB_gameboy_s {
bool turbo;
uint32_t ram_size; // Different between CGB and DMG
uint8_t boot_rom[0x900];
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
} 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_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_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_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 */
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? */
time_t current_time = time(NULL);
while (gb->last_rtc_second < current_time) {
gb->last_rtc_second++;
if (++gb->rtc_seconds == 60)
void GB_rtc_run(GB_gameboy_t *gb)
{
if ((gb->rtc_high & 0x40) == 0) { /* is timer running? */
time_t current_time = time(NULL);
while (gb->last_rtc_second < current_time) {
gb->last_rtc_second++;
if (++gb->rtc_seconds == 60)
{
gb->rtc_seconds = 0;
if (++gb->rtc_minutes == 60)
{
gb->rtc_seconds = 0;
if (++gb->rtc_minutes == 60)
gb->rtc_minutes = 0;
if (++gb->rtc_hours == 24)
{
gb->rtc_minutes = 0;
if (++gb->rtc_hours == 24)
gb->rtc_hours = 0;
if (++gb->rtc_days == 0)
{
gb->rtc_hours = 0;
if (++gb->rtc_days == 0)
if (gb->rtc_high & 1) /* Bit 8 of days*/
{
if (gb->rtc_high & 1) /* Bit 8 of days*/
{
gb->rtc_high |= 0x80; /* Overflow bit */
}
gb->rtc_high ^= 1;
gb->rtc_high |= 0x80; /* Overflow bit */
}
gb->rtc_high ^= 1;
}
}
}

View File

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

View File

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