mirror of https://github.com/bsnes-emu/bsnes.git
Async debugger commands
This commit is contained in:
parent
67f3a3a9d8
commit
aa6438fa06
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue