diff --git a/Cocoa/Document.m b/Cocoa/Document.m index b278630..ea307fb 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -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]; } diff --git a/Core/debugger.c b/Core/debugger.c index 4babcb3..4d278a4 100644 --- a/Core/debugger.c +++ b/Core/debugger.c @@ -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); } } diff --git a/Core/debugger.h b/Core/debugger.h index 78f60ce..7e75185 100644 --- a/Core/debugger.h +++ b/Core/debugger.h @@ -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); diff --git a/Core/display.c b/Core/display.c index c83d398..06ff945 100644 --- a/Core/display.c +++ b/Core/display.c @@ -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) diff --git a/Core/gb.c b/Core/gb.c index 82847e4..1d341e0 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -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; diff --git a/Core/gb.h b/Core/gb.h index f5221ea..24fc291 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -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); diff --git a/Core/timing.c b/Core/timing.c index f07858d..9390c1c 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -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; } } } diff --git a/Core/timing.h b/Core/timing.h index 1e41909..75e66ee 100644 --- a/Core/timing.h +++ b/Core/timing.h @@ -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 */ diff --git a/Core/z80_cpu.c b/Core/z80_cpu.c index d253ffb..b57e979 100644 --- a/Core/z80_cpu.c +++ b/Core/z80_cpu.c @@ -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); + } }