From 225505c32e6dc48c6176d011c0a81537e512096d Mon Sep 17 00:00:00 2001 From: Dagen Brock Date: Wed, 19 Oct 2016 13:12:19 -0500 Subject: [PATCH] omg adding debugger to experimental branch --- src/Makefile | 2 +- src/adb.c | 44 +- src/debug.c | 1584 +++++++++++++++++++++++++++++++++++++++++++++ src/debug.h | 8 + src/engine_c.c | 2 + src/sdl2_driver.c | 83 ++- src/sim65816.c | 138 ++-- src/vars_osx_sdl2 | 4 +- 8 files changed, 1782 insertions(+), 83 deletions(-) create mode 100644 src/debug.c create mode 100644 src/debug.h diff --git a/src/Makefile b/src/Makefile index 70748ad..1bf6e6e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@ # GSport central makefile - you need a 'vars' file linked/copied from a 'vars_xxx' template to build. #@todo: check for vars file and do something useful if missing :P -OBJECTS1 = adb.o clock.o config.o dis.o engine_c.o scc.o iwm.o \ +OBJECTS1 = adb.o clock.o config.o debug.o dis.o engine_c.o scc.o iwm.o \ joystick_driver.o moremem.o paddles.o parallel.o printer.o \ sim65816.o smartport.o sound.o sound_driver.o video.o \ scc_socket_driver.o imagewriter.o scc_imagewriter.o scc_llap.o diff --git a/src/adb.c b/src/adb.c index 58747af..a281b3f 100644 --- a/src/adb.c +++ b/src/adb.c @@ -40,9 +40,9 @@ extern int g_invert_paddles; extern int g_joystick_type; extern int g_a2vid_palette; extern int g_config_control_panel; +extern int g_screenshot_requested; extern word32 g_cfg_vbl_count; extern double g_cur_dcycs; - extern byte *g_slow_memory_ptr; extern byte *g_memory_ptr; extern word32 g_mem_size_total; @@ -1717,7 +1717,7 @@ adb_physical_key_update(int a2code, int is_up) case 0x0c: /* F12 - remap to reset */ a2code = 0x7f; special = 0; - break; + break; default: break; } @@ -1738,22 +1738,23 @@ adb_physical_key_update(int a2code, int is_up) if(special && !is_up) { switch(special) { -// OG Disabled special keys (but warp) -#ifndef ACTIVEGS case 0x04: /* F4 - emulator config panel */ - if (CMD_DOWN) - { - printf("Quit!\n"); - iwm_shut(); - my_exit(1); - } - else - { - cfg_toggle_config_panel(); - } + if (CMD_DOWN) { + printf("Quit!\n"); + iwm_shut(); + my_exit(1); + } + else + { + cfg_toggle_config_panel(); + } break; case 0x05: /* F5 - emulator clipboard paste */ - clipboard_paste(); + if (SHIFT_DOWN) { + g_screenshot_requested = 1; + } else { + clipboard_paste(); + } break; case 0x06: /* F6 - emulator speed */ if(SHIFT_DOWN) { @@ -1767,7 +1768,6 @@ adb_physical_key_update(int a2code, int is_up) printf("g_fast_disk_emul is now %d\n", g_fast_disk_emul); break; -#endif case 0x08: /* F8 - warp pointer */ g_warp_pointer = !g_warp_pointer; if(g_hide_pointer != g_warp_pointer) { @@ -1775,7 +1775,6 @@ adb_physical_key_update(int a2code, int is_up) x_hide_pointer(g_hide_pointer); } break; -#ifndef ACTIVEGS case 0x09: /* F9 - swap paddles */ if(SHIFT_DOWN) { g_swap_paddles = !g_swap_paddles; @@ -1793,18 +1792,17 @@ adb_physical_key_update(int a2code, int is_up) extern void x_toggle_status_lines(); x_toggle_status_lines(); #endif - } else if (CMD_DOWN) { - do_reset(); - return; + } else if (CMD_DOWN) { + do_reset(); + return; } else { change_a2vid_palette((g_a2vid_palette + 1) & 0xf); } break; case 0x0b: /* F11 - full screen */ - g_fullscreen = !g_fullscreen; - x_full_screen(g_fullscreen); + g_fullscreen = !g_fullscreen; + x_full_screen(g_fullscreen); break; -#endif } return; diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 0000000..ba81b52 --- /dev/null +++ b/src/debug.c @@ -0,0 +1,1584 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "defc.h" +#include "debug.h" + +// DISASSEMBLER STUFF +enum { + ABS = 1, ABSX, ABSY, ABSLONG, ABSIND, ABSXIND, IMPLY, ACCUM, IMMED, JUST8, + DLOC, DLOCX, DLOCY, LONG, LONGX, DLOCIND, DLOCINDY, DLOCXIND, DLOCBRAK, + DLOCBRAKY, DISP8, DISP8S, DISP8SINDY, DISP16, MVPMVN, REPVAL, SEPVAL +}; +extern const char * const disas_opcodes[256]; +extern const word32 disas_types[256]; + +// STEPPING/ENGINE STUFF +extern Engine_reg engine; +extern int g_config_control_panel; +extern int g_num_breakpoints; +extern word32 g_breakpts[MAX_BREAK_POINTS]; +int g_dbg_enable_port = 0; +int debug_pause = 0; +int g_dbg_step = 0; +int step_count = 0; + +// emulator command stuff +extern int g_limit_speed; +extern int g_screenshot_requested; + + +typedef enum { + G_DBG_COMMAND_TEST = 0, + G_DBG_COMMAND_HELLO = 1, + G_DBG_COMMAND_STEP = 2, + G_DBG_COMMAND_CONTINUE = 3, + G_DBG_COMMAND_GET_CPU = 4, + G_DBG_COMMAND_SET_CPU = 5, + G_DBG_COMMAND_GET_MEM = 6, + G_DBG_COMMAND_SET_MEM = 7, + G_DBG_COMMAND_ADD_BRK = 8, + G_DBG_COMMAND_DEL_BRK = 9, + G_DBG_COMMAND_GET_BRK = 0xA, + G_DBG_COMMAND_PAUSE = 0xB, // deprecated + G_DBG_COMMAND_DEBUGGER = 0xC, // deprecated + G_DBG_COMMAND_QUIT = 0xD, // deprecated + G_DBG_COMMAND_EMU_CMD = 0xE +} G_DBG_COMMANDS; + + +// incoming commands - 1 char command + 256 char data + +#define DBG_CMD_QUEUE_MAXLEN 50 +int dbg_cmd_queue_len = 0; +struct dbg_cmd +{ + unsigned int command; + char cdata[256]; +}; +struct dbg_cmd dbg_cmd_queue[DBG_CMD_QUEUE_MAXLEN]; + + + +// outgoing response messages - can be batched up +// this one can be bigger because it just has pointers to messages +// and we send multiple messages for debug (a "chain") +#define DBG_MSG_QUEUE_MAXLEN 30 +int dbg_msg_queue_len = 0; +struct dbg_msg +{ + unsigned int msg_size; + char *msg_str; +}; +struct dbg_msg dbg_msg_queue[DBG_MSG_QUEUE_MAXLEN]; + +extern int get_byte_at_address(int addr); +extern void set_byte_at_address(int addr, int value); + +//b64 funcs +int b64encode_len(int); +int b64encode(char *encoded, const char *string, int len); + + +void debug_wait_hello(); +void debug_setup_socket(); +void debug_pop_message(); +void debug_push_message(G_DBG_COMMANDS msg_type, char *msg_ptr, int msg_len); + + +void event_hello(); +void event_test_command(char *str); +void event_pause(); +void event_cpu_info(); +void event_set_cpu(); +void event_get_mem(char *str); +void event_did_step(int step_count); +void event_add_brk(char *str); +void event_del_brk(char *str); +void event_get_brk(); +void event_emu_cmd(char *str); +void event_set_mem(char *str); + + +void api_write_socket(); +void write_array_start(); +void write_array_end(); +void write_array_next(); +int writeStrToClient(int sckt, const char *str); + + + +int do_dis_json(char *buf, word32 kpc, int accsize, int xsize, int op_provided, word32 instr, int chain); + + +// derived from? +/// https://www.ibm.com/support/knowledgecenter/ssw_i5_54/rzab6/poll.htm +#define TRUE 1 +#define FALSE 0 + +int len, rc, on = 1; +int listen_sd = -1, new_sd = -1; +int desc_ready, end_server = FALSE, compress_array = FALSE; +int close_conn; +#define BUFFER_SIZE 128 +char buffer[BUFFER_SIZE]; +char tmp_buffer_4k[4097]; // adding +1 in case someone forgets \0 +char tmp_buffer2_4k[4097]; // adding +1 in case someone forgets \0 + +struct sockaddr_in addr; +int timeout; // poll timeout in ms +struct pollfd fds[200]; +int nfds = 1, current_size = 0, i, j; +int debugger_sd = -1; // this holds our socket file descriptor for the debugger, once attached + + + +/* socket debug version */ +void +do_go_debug() +{ + while (1) { + if (g_dbg_step >= 0) { + if (g_dbg_step == 1) { + g_dbg_step = -1; // we are taking a step, so chill on the next round + } + g_config_control_panel = 0; + clear_halt(); + + + printf("calling run_prog()\n"); + run_prog(); // also calls debug_server_poll() + printf("left run_prog()\n"); + step_count++; + + // so this is a break... + // we need a pool waiter for a continue or go here + + event_did_step(step_count); + } + + timeout = 1000; + debug_server_poll(); + printf("."); + if (debug_events_waiting() > 0) { + timeout = 0; + debug_handle_event(); + } + g_config_control_panel = 1; + } +} + +// removes the message from the front of the queue and slides the rest over +void debug_pop_message() { + if (dbg_cmd_queue_len > 0) { + for (int i = 0; i< dbg_cmd_queue_len; i++) { + dbg_cmd_queue[i]=dbg_cmd_queue[i+1]; // does this copy bad stuff if we hit max? + } + dbg_cmd_queue_len--; + } +} + +// adds message to end of queue and ups the counter +void debug_push_message(G_DBG_COMMANDS msg_type, char *msg_ptr, int msg_len) { + printf("GOT: %d\n", (int) msg_type); + dbg_cmd_queue[dbg_cmd_queue_len].command = msg_type; + memcpy(dbg_cmd_queue[dbg_cmd_queue_len].cdata, msg_ptr, msg_len); // stripping that first char + dbg_cmd_queue[dbg_cmd_queue_len].cdata[msg_len+1] = '\0'; // terminator + dbg_cmd_queue_len++; // INC POINTER +} + +// this should be sufficient :P +int debug_events_waiting() { + //printf("dbg_cmd_queue_len: %d\n", dbg_cmd_queue_len); + return dbg_cmd_queue_len; +} + +// handle event onfront of queue and remove it +void debug_handle_event() +{ + while (debug_events_waiting() > 0) { + switch (dbg_cmd_queue[0].command) { + case G_DBG_COMMAND_TEST: //0 + printf(sprintf("event G_DBG_COMMAND_TEST (%d)", G_DBG_COMMAND_TEST)); + event_test_command(dbg_cmd_queue[0].cdata); + break; + case G_DBG_COMMAND_HELLO: //1 + printf("debug_handle_event(G_DBG_COMMAND_HELLO) %d\n", G_DBG_COMMAND_HELLO); + event_hello(); + break; + case G_DBG_COMMAND_PAUSE: //2 + printf("debug_handle_event(G_DBG_COMMAND_PAUSE) %d\n", G_DBG_COMMAND_PAUSE); + event_pause(); + break; + case G_DBG_COMMAND_STEP: //3 + printf("debug_handle_event(G_DBG_COMMAND_STEP) %d\n", G_DBG_COMMAND_STEP); + if (g_dbg_step == -1) { + g_dbg_step = 1; // take a step + } else { + g_dbg_step = -1; // first one just halts + } + break; + case G_DBG_COMMAND_CONTINUE: //4 + printf("debug_handle_event(G_DBG_COMMAND_CONTINUE) %d\n", G_DBG_COMMAND_CONTINUE); + g_dbg_step = 0; + step_count = 0; + break; + case G_DBG_COMMAND_GET_MEM: //6 + printf("debug_handle_event(G_DBG_COMMAND_GET_MEM) %d\n", G_DBG_COMMAND_GET_MEM); + event_get_mem(dbg_cmd_queue[0].cdata); + break; + case G_DBG_COMMAND_SET_MEM: //7 + printf("debug_handle_event(G_DBG_COMMAND_SET_MEM) %d\n", G_DBG_COMMAND_SET_MEM); + event_set_mem(dbg_cmd_queue[0].cdata); + break; + case G_DBG_COMMAND_QUIT: + printf("debug_handle_event(G_DBG_COMMAND_QUIT) %d\n", G_DBG_COMMAND_QUIT); + exit(0); // HALT! + break; + case G_DBG_COMMAND_DEBUGGER: + printf("debug_handle_event(G_DBG_COMMAND_DEBUGGER) %d\n", G_DBG_COMMAND_DEBUGGER); + do_debug_intfc(); + break; + case G_DBG_COMMAND_SET_CPU: + printf("debug_handle_event(G_DBG_COMMAND_SET_CPU) %d\n", G_DBG_COMMAND_SET_CPU); + event_set_cpu(&dbg_cmd_queue[0].cdata); + break; + case G_DBG_COMMAND_GET_CPU: + printf("debug_handle_event(G_DBG_COMMAND_GET_CPU) %d\n", G_DBG_COMMAND_GET_CPU); + event_cpu_info(); + break; + case G_DBG_COMMAND_ADD_BRK: + printf("debug_handle_event(G_DBG_COMMAND_ADD_BRK) %d\n", G_DBG_COMMAND_ADD_BRK); + event_add_brk(&dbg_cmd_queue[0].cdata); + break; + case G_DBG_COMMAND_DEL_BRK: + printf("debug_handle_event(G_DBG_COMMAND_DEL_BRK) %d\n", G_DBG_COMMAND_DEL_BRK); + event_del_brk(&dbg_cmd_queue[0].cdata); + break; + case G_DBG_COMMAND_GET_BRK: + printf("debug_handle_event(G_DBG_COMMAND_GET_BRK) %d\n", G_DBG_COMMAND_GET_BRK); + event_get_brk(&dbg_cmd_queue[0].cdata); + break; + case G_DBG_COMMAND_EMU_CMD: + printf("debug_handle_event(G_DBG_COMMAND_EMU_CMD) %d\n", G_DBG_COMMAND_EMU_CMD); + event_emu_cmd(&dbg_cmd_queue[0].cdata); + break; + default: + break; + } + debug_pop_message(); + } +} + +void push_api_msg(int size, char *msg_str) +{ + if (dbg_msg_queue_len < DBG_MSG_QUEUE_MAXLEN) { + dbg_msg_queue[dbg_msg_queue_len].msg_size = size; + dbg_msg_queue[dbg_msg_queue_len].msg_str = msg_str; + //printf("Latest message : \n%s\n", dbg_msg_queue[dbg_msg_queue_len].msg_str); + dbg_msg_queue_len++; // INC POINTER + } else { + printf("ABORT! MESSAGE QUEUE FULL!\n"); + } +} + +void api_push_memack() +{ + int size = snprintf(tmp_buffer_4k, sizeof(tmp_buffer_4k),"{\"type\":\"memack\"}"); + char *msg = (char*)malloc(sizeof(char) * size); + strcpy(msg,tmp_buffer_4k); + push_api_msg(size, msg); +} + +void api_push_cpu() +{ + Engine_reg *eptr; + eptr = &engine; + int tmp_acc, tmp_x, tmp_y, tmp_psw; + int kpc, direct_page, dbank, stack; + + kpc = eptr->kpc; + tmp_acc = eptr->acc; + direct_page = eptr->direct; + dbank = eptr->dbank; + stack = eptr->stack; + tmp_x = eptr->xreg; + tmp_y = eptr->yreg; + tmp_psw = eptr->psr; + + int size = snprintf(tmp_buffer_4k, sizeof(tmp_buffer_4k),"{\"type\":\"cpu\",\"data\":{\"K\":\"%02X\",\"PC\":\"%04X\",\"A\":\"%04X\","\ + "\"X\":\"%04X\",\"Y\":\"%04X\",\"S\":\"%04X\",\"D\":\"%04X\",\"B\":\"%02X\",\"PSR\":\"%04X\"}}", + kpc>>16, kpc & 0xffff ,tmp_acc,tmp_x,tmp_y,stack,direct_page,dbank, tmp_psw & 0xFFF); + + char *msg = (char*)malloc(sizeof(char) * size); + strcpy(msg,tmp_buffer_4k); + push_api_msg(size, msg); +} + +void api_push_brk() +{ + int i; + // build our json array of breakpoints + tmp_buffer2_4k[0] = '\0'; // start with empty string + char *str_ptr = tmp_buffer2_4k; + for(i = 0; i < g_num_breakpoints; i++) { + //printf("{\"bp:%02x: %06x\n", i, g_breakpts[i]); + str_ptr += sprintf(str_ptr, "{\"trig\":\"addr\",\"match\":\"%06X\"}", g_breakpts[i]); + if (i < g_num_breakpoints-1) { + str_ptr += sprintf(str_ptr, ","); + } + } + str_ptr = tmp_buffer2_4k; // reset pointer to beginning of string + + int size = snprintf(tmp_buffer_4k, sizeof(tmp_buffer_4k),"{\"type\":\"brk\",\"data\":{\"breakpoints\":[%s]}}", str_ptr); + + char *msg = (char*)malloc(sizeof(char) * size); + strcpy(msg,tmp_buffer_4k); + push_api_msg(size, msg); +} + + +void api_push_stack() +{ + Engine_reg *eptr; + eptr = &engine; + int kpc, stack; + + kpc = eptr->kpc; + stack = eptr->stack; + word32 stack_loc = (kpc & 0xff0000) + (stack & 0xff00); // e.g. 0x00000100 (default) or 0x00FF0100 (GS Boot) + + // build our json array of 256 stack values ($nn00-nnFF) + char *str_ptr = tmp_buffer2_4k; // (1024B) + for (int i = 0; i<256; i++ ) { + unsigned int instruction = (int)get_memory_c(stack_loc+i, 0) & 0xff; + str_ptr += sprintf(str_ptr, "\"%02X\"", instruction); + if (i < 255) { + str_ptr += sprintf(str_ptr, ","); + } + } + str_ptr = tmp_buffer2_4k; // reset pointer to beginning of string + + int size = snprintf(tmp_buffer_4k, sizeof(tmp_buffer_4k),"{\"type\":\"stack\",\"data\":{\"loc\":\"%06X\",\"S\":\"%04X\",\"bytes\":[%s]}}", + stack_loc,stack,str_ptr); + + char *msg = (char*)malloc(sizeof(char) * size); + strcpy(msg,tmp_buffer_4k); + push_api_msg(size, msg); +} + + +void api_push_disassembly_start() +{ + int size = snprintf(tmp_buffer_4k, sizeof(tmp_buffer_4k),"{\"type\":\"dis0\"}"); + char *msg = (char*)malloc(sizeof(char) * size); + strcpy(msg,tmp_buffer_4k); + push_api_msg(size, msg); +} + + +void api_push_disassembly() +{ + int byte_size; + int size_mem_imm, size_x_imm; + + + // check accumulator size + size_mem_imm = 2; + if(engine.psr & 0x20) { + size_mem_imm = 1; + } + // check xy size + size_x_imm = 2; + if(engine.psr & 0x10) { + size_x_imm = 1; + } + + //disassemble chain from now -> to -> future + int disassemble_next = 10; // how many instructions + int kpc = engine.kpc; // get starting address + printf("KPC: %06X", kpc); + for (int i = 0; i= 0 && int_value <= 3) { + printf("Changing speed from %d to -> %d\n", g_limit_speed, int_value); + g_limit_speed = int_value; + } else { + printf("Specified speed out of range (%d)\n", int_value); + } + break; + case 'v': + g_screenshot_requested = 1; + break; + } +} + +void event_emu_cmd(char *str) +{ + // split our commands on spaces + char cmd_char = NULL; + char *cmd_data = NULL; + + char * pch; + pch = strtok (str," "); + while (pch != NULL) + { + cmd_char = pch[0]; + cmd_data = pch+1; + if (cmd_data[0] == '\0') { + cmd_data = NULL; + } + // for each token go try to handle it + handle_emu_cmd(cmd_char, cmd_data); + + pch = strtok (NULL, " "); + } + return; +} + + +void event_add_brk(char *str) +{ + int addr = 0; + sscanf(str, "%06X", &addr); + addr = addr & 0xFFFFFF; // 24 bit KPC address for 65816 + set_bp(addr); + api_push_brk(); + api_write_socket(); + + return; +} + +void event_del_brk(char *str) +{ + int addr = 0; + sscanf(str, "%06X", &addr); + addr = addr & 0xFFFFFF; // 24 bit KPC address for 65816 + delete_bp(addr); + api_push_brk(); + api_write_socket(); + + return; +} + +void event_get_brk(char *str) +{ + api_push_brk(); + api_write_socket(); + + return; +} + + + +void handle_cpu_cmd(char cmd_char, char *cmd_data) +{ + Engine_reg *eptr; + eptr = &engine; + int new_acc, new_x, new_y; + int new_kpc, new_d, new_b, new_s, new_psr; + + switch (cmd_char) { + case 'k': + sscanf(cmd_data, "%06x", &new_kpc); + new_kpc &= 0xFFFFFF; // 24-bit clamp + eptr->kpc = new_kpc; + break; + case 'a': + sscanf(cmd_data, "%04x", &new_acc); + new_acc &= 0xFFFF; // 24-bit clamp + eptr->acc = new_acc; + break; + case 'x': + sscanf(cmd_data, "%04x", &new_x); + new_x &= 0xFFFF; // 24-bit clamp + eptr->xreg = new_x; + break; + case 'y': + sscanf(cmd_data, "%04x", &new_y); + new_y &= 0xFFFF; // 24-bit clamp + eptr->yreg = new_y; + break; + case 's': + sscanf(cmd_data, "%04x", &new_s); + new_s &= 0xFFFF; // 24-bit clamp + eptr->stack = new_s; + break; + case 'd': + sscanf(cmd_data, "%04x", &new_d); + new_d &= 0xFFFF; // 24-bit clamp + eptr->direct = new_d; + break; + case 'b': + sscanf(cmd_data, "%02x", &new_b); + new_b &= 0xFF; // 24-bit clamp + eptr->dbank = new_b; + break; + case 'p': + sscanf(cmd_data, "%04x", &new_psr); + new_psr &= 0xFFFF; // 24-bit clamp + eptr->psr = new_psr; + break; + default: + printf("UNKNOWN CPU COMMAND\n"); + break; + } +} + + +void event_set_cpu(char *str) +{ + // split our commands on spaces + char cmd_char = NULL; + char *cmd_data = NULL; + + char * pch; + pch = strtok (str," "); + while (pch != NULL) + { + cmd_char = pch[0]; + cmd_data = pch+1; + if (cmd_data[0] == '\0') { + cmd_data = NULL; + } + // for each token go try to handle it + handle_cpu_cmd(cmd_char, cmd_data); + + pch = strtok (NULL, " "); + } + + api_push_stack(); + api_push_cpu(); + api_push_disassembly(); + + api_write_socket(); +} + + + +void debug_init() +{ + if (g_dbg_enable_port > 0) { + // g_dbg_enable_port should be enabled by + printf("Debug port enabled on: %d\n", g_dbg_enable_port); + debug_setup_socket(); + debug_server_poll(); + debug_wait_hello(); + // message is not popped off here because default behavior to send our cpu hello info to client + } else { + end_server = TRUE; + printf("Debug port not enabled\n"); + } +} + + +void debug_setup_socket() +{ + /*************************************************************/ + /* Create an AF_INET stream socket to receive incoming */ + /* connections on */ + /*************************************************************/ + listen_sd = socket(AF_INET, SOCK_STREAM, 0); + if (listen_sd < 0) { + perror("socket() failed"); + exit(-1); + } + + /*************************************************************/ + /* Allow socket descriptor to be reuseable */ + /*************************************************************/ + rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); + if (rc < 0) { + perror("setsockopt() failed"); + close(listen_sd); + exit(-1); + } + + /*************************************************************/ + /* Set socket to be nonblocking. All of the sockets for */ + /* the incoming connections will also be nonblocking since */ + /* they will inherit that state from the listening socket. */ + /*************************************************************/ + //rc = ioctl(listen_sd, FIONBIO, (char *)&on); + int flags = fcntl(listen_sd, F_GETFL, 0); + rc = fcntl(listen_sd, F_SETFL, flags | O_NONBLOCK); + if (rc < 0) { + perror("ioctl()/fcntl() failed"); + close(listen_sd); + exit(-1); + } + + /*************************************************************/ + /* Bind the socket */ + /*************************************************************/ + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(g_dbg_enable_port); + rc = bind(listen_sd, (struct sockaddr *)&addr, sizeof(addr)); + if (rc < 0) { + perror("bind() failed"); + close(listen_sd); + exit(-1); + } + + /*************************************************************/ + /* Set the listen back log */ + /*************************************************************/ + rc = listen(listen_sd, 32); + if (rc < 0) + { + perror("listen() failed"); + close(listen_sd); + exit(-1); + } + + /*************************************************************/ + /* Initialize the pollfd structure */ + /*************************************************************/ + memset(fds, 0 , sizeof(fds)); + + /*************************************************************/ + /* Set up the initial listening socket */ + /*************************************************************/ + fds[0].fd = listen_sd; + fds[0].events = POLLIN; + /*************************************************************/ + /* Initialize the timeout to 3 minutes. If no */ + /* activity after 3 minutes this program will end. */ + /* timeout value is based on milliseconds. */ + /*************************************************************/ + timeout = (3 * 60 * 1000); +} + +// builds our big json array of commands eg "[{},{},{},...]" +void api_write_socket() +{ + const char *comma = ","; + const char *lbrack = "["; + const char *rbrack = "]\r\n\r\n"; + unsigned int message_size = 0; + char *message_string = NULL; + for (int i = 0; i < dbg_msg_queue_len; i++) { + message_size = message_size + dbg_msg_queue[i].msg_size; // for sub-string sizes + } + message_size += dbg_msg_queue_len; // for "," + message_size += strlen(lbrack) + strlen(rbrack); // for "[]" + "\0" + message_string = malloc(sizeof(char)*message_size); + message_string[0] = '\0'; // necessary? + + char *str_ptr = message_string; // message_string is alloc'd above to message_size + str_ptr = strcat(str_ptr, lbrack); // "[" + for (int i = 0; i < dbg_msg_queue_len; i++) { + str_ptr = strcat(str_ptr, dbg_msg_queue[i].msg_str); + if (dbg_msg_queue_len > 1 && i < dbg_msg_queue_len-1) { // only split with comma if more than 1 item and not last item + str_ptr = strcat(str_ptr, comma); // "," + } + free(dbg_msg_queue[i].msg_str); // here we go! oh boy! + } + str_ptr = strcat(str_ptr, rbrack); // "]" + // message_string is now built! we can send it. + dbg_msg_queue_len = 0; // clear msg queue + + writeStrToClient(debugger_sd, message_string); // ignores result + free(message_string); // assuming it was all written :P +} + +void write_array_start() +{ + const char *brack = "["; + write(debugger_sd, brack, strlen(brack)); +} + + +void write_array_end() +{ + const char *brack = "]\r\n\r\n"; + write(debugger_sd, brack, strlen(brack)); +} + + +void write_array_next() +{ + const char *com = ", "; // i'm so neat + write(debugger_sd, com, strlen(com)); +} + +// // write(debugger_sd, buffer, strlen(buffer)); +//also for base 64 http://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c +//http://stackoverflow.com/questions/32749925/sending-a-file-over-a-tcp-ip-socket-web-server +int writeDataToClient(int sckt, const void *data, int datalen) +{ + const char *pdata = (const char*) data; + + while (datalen > 0){ + int numSent = send(sckt, pdata, datalen, 0); + if (numSent <= 0){ + if (numSent == 0){ + printf("The client was not written to: disconnected\n"); + } else { + perror("The client was not written to"); + } + return FALSE; + } + pdata += numSent; + datalen -= numSent; + } + + return TRUE; +} + + +int writeStrToClient(int sckt, const char *str) +{ + return writeDataToClient(sckt, str, strlen(str)); +} + +// @todo: probably clean up- this was a hack to allow preloading commands +void debug_wait_hello() +{ + int hello_received = FALSE; + timeout = 1000; // 1 sec + while (hello_received == FALSE) { + debug_server_poll(); + if (debug_events_waiting() > 0) { + if (dbg_cmd_queue[0].command == G_DBG_COMMAND_HELLO) { + hello_received = TRUE; + } else { + debug_handle_event(); + } + } + + } + timeout = 0; // instantaneous +} + +void debug_server_poll() +{ + if (end_server == FALSE) { + /***********************************************************/ + /* Call poll() and wait for it to complete/timeout. */ + /***********************************************************/ + //printf("Waiting on poll()...\n"); + //printf("Waiting on debugger connection\n"); + rc = poll(fds, nfds, timeout); + + /***********************************************************/ + /* Check to see if the poll call failed. */ + /***********************************************************/ + if (rc < 0) + { + perror(" poll() failed"); + return; // @todo: break/exit? + } + + /***********************************************************/ + /* Check to see if the 3 minute time out expired. */ + /***********************************************************/ + if (rc == 0) + { + //printf(" poll() timed out. End program.\n"); + //printf("~"); + return; // @todo: break/exit? + } + + + /***********************************************************/ + /* One or more descriptors are readable. Need to */ + /* determine which ones they are. */ + /***********************************************************/ + current_size = nfds; + for (i = 0; i < current_size; i++) + { + /*********************************************************/ + /* Loop through to find the descriptors that returned */ + /* POLLIN and determine whether it's the listening */ + /* or the active connection. */ + /*********************************************************/ + if(fds[i].revents == 0) + continue; + + /*********************************************************/ + /* If revents is not POLLIN, it's an unexpected result, */ + /* log and end the server. */ + /*********************************************************/ + if(fds[i].revents != POLLIN) + { + printf(" Error! revents = %d\n", fds[i].revents); + end_server = TRUE; + break; + + } + if (fds[i].fd == listen_sd) + { + /*******************************************************/ + /* Listening descriptor is readable. */ + /*******************************************************/ + printf(" Listening socket is readable\n"); + + /*******************************************************/ + /* Accept all incoming connections that are */ + /* queued up on the listening socket before we */ + /* loop back and call poll again. */ + /*******************************************************/ + do + { + /*****************************************************/ + /* Accept each incoming connection. If */ + /* accept fails with EWOULDBLOCK, then we */ + /* have accepted all of them. Any other */ + /* failure on accept will cause us to end the */ + /* server. */ + /*****************************************************/ + new_sd = accept(listen_sd, NULL, NULL); + if (new_sd < 0) + { + if (errno != EWOULDBLOCK) + { + perror(" accept() failed"); + end_server = TRUE; + } + break; + } + + /*****************************************************/ + /* Add the new incoming connection to the */ + /* pollfd structure */ + /*****************************************************/ + printf(" New incoming connection - %d\n", new_sd); + fds[nfds].fd = new_sd; + fds[nfds].events = POLLIN; + nfds++; + + // just set it and forget it :P + debugger_sd = new_sd; + timeout = 0; + + /*****************************************************/ + /* Loop back up and accept another incoming */ + /* connection */ + /*****************************************************/ + } while (new_sd != -1); + } + + /*********************************************************/ + /* This is not the listening socket, therefore an */ + /* existing connection must be readable */ + /*********************************************************/ + + else + { + //printf(" Descriptor %d is readable\n", fds[i].fd); + close_conn = FALSE; + /*******************************************************/ + /* Receive all incoming data on this socket */ + /* before we loop back and call poll again. */ + /*******************************************************/ + + do + { + /*****************************************************/ + /* Receive data on this connection until the */ + /* recv fails with EWOULDBLOCK. If any other */ + /* failure occurs, we will close the */ + /* connection. */ + /*****************************************************/ + rc = recv(fds[i].fd, buffer, sizeof(buffer), 0); + if (rc < 0) { + if (errno != EWOULDBLOCK) + { + perror(" recv() failed"); + close_conn = TRUE; + } + break; + } + + + /*****************************************************/ + /* Check to see if the connection has been */ + /* closed by the client */ + /*****************************************************/ + if (rc == 0) { + printf(" Connection closed\n"); + close_conn = TRUE; + end_server = TRUE; + break; + } + + /*****************************************************/ + /* Data was received */ + /*****************************************************/ + len = rc; + //printf("\n\n === debug_server_poll() %d bytes received \n", len); + //printf(" ==== current queue len: %d\n", dbg_cmd_queue_len); + char *mesg_ptr = buffer; + char *split_ptr = strchr(mesg_ptr, '\n'); + + int mesg_len = len-1; // stripping that first char + if(split_ptr) { + int index = split_ptr - buffer; + //printf("Found: %d\n", index); + mesg_len = index - 1; // stripping that first char + } + int debug_echo = FALSE; + while (mesg_ptr < buffer + len - 1) { + // DO DEBUG QUEUE + if (dbg_cmd_queue_len < DBG_CMD_QUEUE_MAXLEN) { + switch (mesg_ptr[0]) { + case '0': + debug_push_message(G_DBG_COMMAND_TEST, mesg_ptr+1, mesg_len); + break; + case '1': + debug_push_message(G_DBG_COMMAND_HELLO, mesg_ptr+1, mesg_len); + break; + case '2': + debug_push_message(G_DBG_COMMAND_STEP, mesg_ptr+1, mesg_len); + break; + case '3': + debug_push_message(G_DBG_COMMAND_CONTINUE, mesg_ptr+1, mesg_len); + break; + case '4': + debug_push_message(G_DBG_COMMAND_GET_CPU, mesg_ptr+1, mesg_len); + break; + case '5': + debug_push_message(G_DBG_COMMAND_SET_CPU, mesg_ptr+1, mesg_len); + break; + case '6': + debug_push_message(G_DBG_COMMAND_GET_MEM, mesg_ptr+1, mesg_len); + break; + case '7': + debug_push_message(G_DBG_COMMAND_SET_MEM, mesg_ptr+1, mesg_len); + break; + case '8': + debug_push_message(G_DBG_COMMAND_ADD_BRK, mesg_ptr+1, mesg_len); + break; + case '9': + debug_push_message(G_DBG_COMMAND_DEL_BRK, mesg_ptr+1, mesg_len); + break; + case 'a': + debug_push_message(G_DBG_COMMAND_GET_BRK, mesg_ptr+1, mesg_len); + break; + + case 'b': // DEPRECATED + debug_push_message(G_DBG_COMMAND_PAUSE, mesg_ptr+1, mesg_len); + break; + case 'c': // DEPRECATED + debug_push_message(G_DBG_COMMAND_DEBUGGER, mesg_ptr+1, mesg_len); + break; + case 'd': // DEPRECATED ???? + debug_push_message(G_DBG_COMMAND_QUIT, mesg_ptr+1, mesg_len); + break; + + case 'e': + debug_push_message(G_DBG_COMMAND_EMU_CMD, mesg_ptr+1, mesg_len); + break; + + default: + printf("UNKNOWN COMMAND - DISCARDED\n"); + } + + + } else { + printf("COMMAND QUEUE FULL! ABORT!\n"); + // @TODO probably send error response + } + + mesg_ptr += mesg_len + 2; // +1 for command char and +1 for '\n' + // printf(mesg_ptr); + split_ptr = strchr(mesg_ptr, '\n'); + if(split_ptr) { + int index = split_ptr - mesg_ptr; + // printf("Found: %d\n", index); + mesg_len = index - 1; // stripping that first char + } + } + /*****************************************************/ + /* Echo the data back to the client */ + /*****************************************************/ + if (debug_echo) { + rc = send(fds[i].fd, buffer, len, 0); + if (rc < 0) + { + perror(" send() failed"); + close_conn = TRUE; + break; + } + } + + // clear our buffer + memset(buffer, 0, sizeof(buffer)); + + } while(TRUE); + + /*******************************************************/ + /* If the close_conn flag was turned on, we need */ + /* to clean up this active connection. This */ + /* clean up process includes removing the */ + /* descriptor. */ + /*******************************************************/ + if (close_conn) + { + close(fds[i].fd); + fds[i].fd = -1; + compress_array = TRUE; + } + + + } /* End of existing connection is readable */ + } /* End of loop through pollable descriptors */ + + /***********************************************************/ + /* If the compress_array flag was turned on, we need */ + /* to squeeze together the array and decrement the number */ + /* of file descriptors. We do not need to move back the */ + /* events and revents fields because the events will always*/ + /* be POLLIN in this case, and revents is output. */ + /***********************************************************/ + if (compress_array) { + compress_array = FALSE; + for (i = 0; i < nfds; i++) { + if (fds[i].fd == -1) { + for(j = i; j < nfds; j++) { + fds[j].fd = fds[j+1].fd; + } + nfds--; + } + } + } + } else { + // end_server == TRUE // @TODO handle server exit (and multiple clients?) + /*************************************************************/ + /* Clean up all of the sockets that are open */ + /*************************************************************/ + for (i = 0; i < nfds; i++) { + if(fds[i].fd >= 0) + close(fds[i].fd); + } + } +} + + +int do_dis_json(char *buf, word32 kpc, int accsize, int xsize, + int op_provided, word32 instr, int chain) + { + char buf_instructions[5*5]; // '["12","DE","AB"]' + char buf_disasm[50]; + + + const char *out; + int args, type; + int opcode; + word32 val; + word32 oldkpc; + word32 dtype; + int signed_val; + + oldkpc = kpc; + if(op_provided) { + opcode = (instr >> 24) & 0xff; + } else { + opcode = (int)get_memory_c(kpc, 0) & 0xff; + } + + kpc++; + + dtype = disas_types[opcode]; + out = disas_opcodes[opcode]; + type = dtype & 0xff; + args = dtype >> 8; + + if(args > 3) { + if(args == 4) { + args = accsize; + } else if(args == 5) { + args = xsize; + } + } + + val = -1; + switch(args) { + case 0: + val = 0; + break; + case 1: + if(op_provided) { + val = instr & 0xff; + } else { + val = get_memory_c(kpc, 0); + } + break; + case 2: + if(op_provided) { + val = instr & 0xffff; + } else { + val = get_memory16_c(kpc, 0); + } + break; + case 3: + if(op_provided) { + val = instr & 0xffffff; + } else { + val = get_memory24_c(kpc, 0); + } + break; + default: + fprintf(stderr, "args out of range: %d, opcode: %08x\n", + args, opcode); + break; + } + kpc += args; + + if(!op_provided) { + instr = (opcode << 24) | (val & 0xffffff); + } + + switch(type) { + case ABS: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%04x",out,val); + break; + case ABSX: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%04x,X",out,val); + break; + case ABSY: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%04x,Y",out,val); + break; + case ABSLONG: + if(args != 3) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%06x",out,val); + break; + case ABSIND: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s ($%04x)",out,val); + break; + case ABSXIND: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s ($%04x,X)",out,val); + break; + case IMPLY: + if(args != 0) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s",out); + break; + case ACCUM: + if(args != 0) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s",out); + break; + case IMMED: + if(args == 1) { + sprintf(buf_disasm,"%s #$%02x",out,val); + } else if(args == 2) { + sprintf(buf_disasm,"%s #$%04x",out,val); + } else { + printf("arg # mismatch for opcode %x\n", opcode); + } + break; + case JUST8: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%02x",out,val); + break; + case DLOC: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%02x",out,val); + break; + case DLOCX: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%02x,X",out,val); + break; + case DLOCY: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%02x,Y",out,val); + break; + case LONG: + if(args != 3) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%06x",out,val); + break; + case LONGX: + if(args != 3) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%06x,X",out,val); + break; + case DLOCIND: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s ($%02x)",out,val); + break; + case DLOCINDY: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s ($%02x),Y",out,val); + break; + case DLOCXIND: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s ($%02x,X)",out,val); + break; + case DLOCBRAK: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s [$%02x]",out,val); + break; + case DLOCBRAKY: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s [$%02x],y",out,val); + break; + case DISP8: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + signed_val = (signed char)val; + sprintf(buf_disasm,"%s $%04x",out, + (word32)(kpc+(signed_val)) & 0xffff); + break; + case DISP8S: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%02x,S",out,(word32)(byte)(val)); + break; + case DISP8SINDY: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s ($%02x,S),Y",out,(word32)(byte)(val)); + break; + case DISP16: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%04x", out, + (word32)(kpc+(signed)(word16)(val)) & 0xffff); + break; + case MVPMVN: + if(args != 2) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s $%02x,$%02x",out,val&0xff,val>>8); + break; + case SEPVAL: + case REPVAL: + if(args != 1) { + printf("arg # mismatch for opcode %x\n", opcode); + } + sprintf(buf_disasm,"%s #$%02x",out,val); + break; + default: + printf("argument type: %d unexpected\n", type); + break; + } + + + // gross + word32 operand = instr; + opcode = (operand >> 24) & 0xff; + switch (args+1) { + case 1: + snprintf(buf_instructions, sizeof(buf_instructions),"[\"%02X\"]", opcode); + break; + case 2: + snprintf(buf_instructions, sizeof(buf_instructions),"[\"%02X\",\"%02X\"]", opcode, instr & 0xff); + break; + case 3: + snprintf(buf_instructions, sizeof(buf_instructions),"[\"%02X\",\"%02X\",\"%02X\"]", opcode, instr & 0xff, (instr & 0xff00) >> 8); + break; + case 4: + snprintf(buf_instructions, sizeof(buf_instructions),"[\"%02X\",\"%02X\",\"%02X\",\"%02X\"]", opcode, instr & 0xff, (instr & 0xff00) >> 8, (instr & 0xff0000) >> 16); + break; + default: + break; + } + + + // @TODO: FIX!!! NEEDS REAL BUFFER SIZE, note magic 1024 + snprintf(buf, 1024,"{\"type\":\"dis\",\"data\":{\"K\":\"%02X\",\"PC\":\"%04X\",\"bytes\":%s,"\ + "\"disasm\":\"%s\",\"chain\":\"%d\"}}", + oldkpc>>16, oldkpc & 0xffff ,buf_instructions, buf_disasm, chain); + return(args+1); + } + + // BASE 64 ENCODER (FOR MEMORY DUMPS) + static const char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + int b64encode_len(int len) + { + return ((len + 2) / 3 * 4) + 1; + } + + int b64encode(char *encoded, const char *string, int len) + { + int i; + char *p; + + p = encoded; + // encode a chunk of 4 chars + for (i = 0; i < len - 2; i += 3) { + *p++ = b64chars[(string[i] >> 2) & 0x3F]; + *p++ = b64chars[((string[i] & 0x3) << 4) | ((int) (string[i + 1] & 0xF0) >> 4)]; + *p++ = b64chars[((string[i + 1] & 0xF) << 2) | ((int) (string[i + 2] & 0xC0) >> 6)]; + *p++ = b64chars[string[i + 2] & 0x3F]; + } + // end chunk + if (i < len) { + *p++ = b64chars[(string[i] >> 2) & 0x3F]; + + if (i == (len - 1)) { + *p++ = b64chars[((string[i] & 0x3) << 4)]; + *p++ = '='; + } else { + *p++ = b64chars[((string[i] & 0x3) << 4) | + ((int) (string[i + 1] & 0xF0) >> 4)]; + *p++ = b64chars[((string[i + 1] & 0xF) << 2)]; + } + *p++ = '='; + } + // terminator + *p++ = '\0'; + //printf("ENCODED : %d\n", p-encoded); + return p - encoded; + } diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..d198385 --- /dev/null +++ b/src/debug.h @@ -0,0 +1,8 @@ +extern void debug_init(); +extern void debug_poll_only(); +extern void debug_cpu_test(); + +// used in sim65816.c +void debug_server_poll(); +int debug_events_waiting(); +void debug_handle_event(); diff --git a/src/engine_c.c b/src/engine_c.c index 2a5664d..a68b5ee 100644 --- a/src/engine_c.c +++ b/src/engine_c.c @@ -40,6 +40,7 @@ # define FCYCS_PTR_FCYCLES_ROUND_SLOW #endif +extern int g_dbg_step; extern int halt_sim; extern int g_code_red; extern int g_ignore_halts; @@ -376,6 +377,7 @@ check_breakpoints(word32 addr) count = g_num_breakpoints; for(i = 0; i < count; i++) { if((g_breakpts[i] & 0xffffff) == addr) { + g_dbg_step = -2; halt2_printf("Hit breakpoint at %06x\n", addr); } } diff --git a/src/sdl2_driver.c b/src/sdl2_driver.c index 2fac409..e8e622b 100644 --- a/src/sdl2_driver.c +++ b/src/sdl2_driver.c @@ -23,6 +23,8 @@ // @todo: mouse clip bugs.. great western shootout. Paint 8/16. still in win32 #include "SDL.h" +#include "SDL_image.h" + #include #include #include @@ -46,6 +48,12 @@ int g_win_status_debug_request = 0; // Desired visibility of status lines. int g_screen_mdepth = 0; int kb_shift_control_state = 0; +void x_take_screenshot(); // screenshot stuff +int g_screenshot_requested = 0; // DB to know if we want to save a screenshot +extern char g_config_gsplus_name[]; +int screenshot_index = 0; // allows us to save time by not scanning from 0 each time +char screenshot_filename[256]; + extern int g_screen_depth; extern int g_quit_sim_now; extern int g_border_sides_refresh_needed; @@ -341,6 +349,12 @@ void sdl_push_kimage(Kimage *kimage_ptr, SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); + if (g_screenshot_requested) { + x_take_screenshot(); + g_screenshot_requested = 0; + } + + } @@ -583,6 +597,74 @@ int clipboard_get_char() { return g_clipboard[g_clipboard_pos++] | 0x80; } +void x_full_screen(int do_full) { + if (do_full) { + SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); + } else { + SDL_SetWindowFullscreen(window, 0); + SDL_SetWindowSize(window, BASE_WINDOW_WIDTH, X_A2_WINDOW_HEIGHT); + } +} + +int file_exists(char *fname){ + if( access( fname, F_OK ) != -1 ) { + return 1; // file exists + } else { + return 0; // file doesn't exist + } +} + +void make_next_screenshot_filename() +{ + char filepart[256]; + char filename[256]; + + + int available_filename = 0; + while (!available_filename) { + // get location of '.' + char *dotptr = strchr(g_config_gsplus_name, '.'); + int index = dotptr - g_config_gsplus_name; + strncpy(filepart, g_config_gsplus_name, index); + filepart[index] = '\0'; //terminator + sprintf(filename, "%s%04d.png",filepart,screenshot_index); + + if (file_exists(filename)) { + //printf("Found existing %s\n", filename); + screenshot_index++; + } else { + //printf("Available filename: %s\n", filename); + available_filename = 1; + } + } + strcpy(screenshot_filename, filename); +} + +// @todo: some error with writing data direct to png. output is empty/transparent? +// workaround is this horrible hack of saving the bmp -> load bmp -> save png +void x_take_screenshot() { + make_next_screenshot_filename(); + + SDL_Surface *sshot = SDL_CreateRGBSurface(0, BASE_WINDOW_WIDTH, X_A2_WINDOW_HEIGHT, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); + SDL_LockSurface(sshot); + int read = SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_ARGB8888, sshot->pixels, sshot->pitch); + if (read != 0) { + printf("READPXL FAIL!\n%s\n", SDL_GetError()); + } + SDL_SaveBMP(sshot, "screenshot.bmp"); + SDL_UnlockSurface(sshot); + SDL_FreeSurface(sshot); + + SDL_Surface *s = SDL_CreateRGBSurface(0, BASE_WINDOW_WIDTH, X_A2_WINDOW_HEIGHT, + 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000); + if (s) { + SDL_Surface * image = SDL_LoadBMP("screenshot.bmp"); + IMG_SavePNG(image, screenshot_filename); + SDL_FreeSurface(image); + + } + SDL_FreeSurface(s); +} @@ -596,7 +678,6 @@ void x_redraw_status_lines() { } void x_hide_pointer(int do_hide) { } void x_auto_repeat_on(int must) { } void x_auto_repeat_off(int must) { } -void x_full_screen(int do_full) { } // OG Adding release void x_release_kimage(Kimage* kimage_ptr) { } // OG Addding ratio diff --git a/src/sim65816.c b/src/sim65816.c index 612e8bb..6b3be92 100644 --- a/src/sim65816.c +++ b/src/sim65816.c @@ -22,10 +22,10 @@ */ #include - #include "defc.h" #include "printer.h" #include "imagewriter.h" +#include "debug.h" extern const char *g_config_gsplus_name_list[]; #ifdef UNDER_CE @@ -49,7 +49,6 @@ extern const char *g_config_gsplus_name_list[]; int g_speed_fast ; // OG Expose fast parameter int g_initialized = 0; // OG To know if the emulator has finalized its initialization int g_accept_events = 0; // OG To know if the emulator is ready to accept external events - char g_argv0_path[256] = "./"; const char *g_gsplus_default_paths[] = { "", "./", "${HOME}/","${PWD}/", @@ -74,6 +73,8 @@ const char *g_gsplus_default_paths[] = { "", "./", "${HOME}/","${PWD}/", #define EV_VID_UPD 7 extern int g_stepping; +extern int g_dbg_step; + extern int g_c068_statereg; extern int g_cur_a2_stat; @@ -114,6 +115,7 @@ extern int g_config_control_panel; extern int g_audio_enable; extern int g_preferred_rate; +extern int g_dbg_enable_port; void U_STACK_TRACE(); @@ -159,7 +161,7 @@ int g_imagewriter_paper = 0; int g_imagewriter_banner = 0; int g_config_iwm_vbl_count = 0; -const char g_gsplus_version_str[] = "0.10a"; +const char g_gsplus_version_str[] = "0.12s"; // 12 socket debug int g_pause=0; // OG Added pause #define START_DCYCS (0.0) @@ -242,64 +244,63 @@ Data_log *g_log_data_end_ptr = &(g_data_log_array[PC_LOG_LEN]); // OG Added sim65816_initglobals() void sim65816_initglobals() { + g_fcycles_stop = 0.0; + halt_sim = 0; + enter_debug = 0; + g_rom_version = -1; + g_user_halt_bad = 0; + g_halt_on_bad_read = 0; + g_ignore_bad_acc = 1; + g_ignore_halts = 1; + g_code_red = 0; + g_code_yellow = 0; + g_use_alib = 0; + g_iw2_emul = 0; + g_serial_out_masking = 0; + //g_serial_modem[2] = { 0, 1 }; - g_fcycles_stop = 0.0; - halt_sim = 0; - enter_debug = 0; - g_rom_version = -1; - g_user_halt_bad = 0; - g_halt_on_bad_read = 0; - g_ignore_bad_acc = 1; - g_ignore_halts = 1; - g_code_red = 0; - g_code_yellow = 0; - g_use_alib = 0; - g_iw2_emul = 0; - g_serial_out_masking = 0; - //g_serial_modem[2] = { 0, 1 }; + g_config_iwm_vbl_count = 0; - g_config_iwm_vbl_count = 0; + g_pause=0; - g_pause=0; + g_last_vbl_dcycs = START_DCYCS; + g_cur_dcycs = START_DCYCS; - g_last_vbl_dcycs = START_DCYCS; - g_cur_dcycs = START_DCYCS; - - g_last_vbl_dadjcycs = 0.0; - g_dadjcycs = 0.0; + g_last_vbl_dadjcycs = 0.0; + g_dadjcycs = 0.0; - g_wait_pending = 0; - g_stp_pending = 0; + g_wait_pending = 0; + g_stp_pending = 0; - g_num_irq = 0; - g_num_brk = 0; - g_num_cop = 0; - g_num_enter_engine = 0; - g_io_amt = 0; - g_engine_action = 0; - g_engine_halt_event = 0; - g_engine_scan_int = 0; - g_engine_doc_int = 0; + g_num_irq = 0; + g_num_brk = 0; + g_num_cop = 0; + g_num_enter_engine = 0; + g_io_amt = 0; + g_engine_action = 0; + g_engine_halt_event = 0; + g_engine_scan_int = 0; + g_engine_doc_int = 0; - g_testing = 0; - g_testing_enabled = 0; + g_testing = 0; + g_testing_enabled = 0; - g_debug_file_fd = -1; - g_fatal_log = -1; + g_debug_file_fd = -1; + g_fatal_log = -1; - g_25sec_cntr = 0; - g_1sec_cntr = 0; + g_25sec_cntr = 0; + g_1sec_cntr = 0; - g_dnatcycs_1sec = 0.0; - g_natcycs_lastvbl = 0; + g_dnatcycs_1sec = 0.0; + g_natcycs_lastvbl = 0; - Verbose = 0; - Halt_on = 0; + Verbose = 0; + Halt_on = 0; - g_mem_size_base = 256*1024; /* size of motherboard memory */ - g_mem_size_exp = 8*1024*1024; /* size of expansion RAM card */ - g_mem_size_total = 256*1024; /* Total contiguous RAM from 0 */ + g_mem_size_base = 256*1024; /* size of motherboard memory */ + g_mem_size_exp = 8*1024*1024; /* size of expansion RAM card */ + g_mem_size_total = 256*1024; /* Total contiguous RAM from 0 */ } void @@ -788,6 +789,7 @@ do_reset() engine.kpc = get_memory16_c(0x00fffc, 0); g_stepping = 0; + g_dbg_step = 0; if (g_irq_pending) halt_printf("*** irq remainings...\n"); @@ -1047,7 +1049,14 @@ gsplusmain(int argc, char **argv) g_config_gsplus_name_list[0] = argv[i+1]; // super dangerous ? g_config_gsplus_name_list[1] = 0; // terminate string array i++; - + } else if(!strcmp("-debugport", argv[i])) { // Debug port passed + if((i+1) >= argc) { + printf("Missing argument\n"); + exit(1); + } + g_dbg_enable_port = strtol(argv[i+1], 0, 0); + printf("Debug port: %d\n", g_dbg_enable_port); + i++; } else { if ((i == (argc - 1)) && (strncmp("-", argv[i], 1) != 0)) { final_arg = argv[i]; @@ -1088,6 +1097,7 @@ gsplusmain(int argc, char **argv) } iwm_init(); + debug_init(); // enable socket debugger if configured config_init(); // If the final argument was not a switch, then treat it like a disk image filename to insert if (final_arg) { @@ -1129,6 +1139,7 @@ gsplusmain(int argc, char **argv) init_reg(); clear_halt(); + initialize_events(); video_init(); @@ -1143,15 +1154,20 @@ gsplusmain(int argc, char **argv) do_reset(); g_stepping = 0; + g_dbg_step = 0; // OG Notify emulator has been initialized and ready to accept external events g_initialized = 1; g_accept_events = 1; - do_go(); - - /* If we get here, we hit a breakpoint, call debug intfc */ - do_debug_intfc(); + // Call one of two main run_prog() routines + if (g_dbg_enable_port) { + do_go_debug(); + } else { + do_go(); + /* If we get here, we hit a breakpoint, call debug intfc */ + do_debug_intfc(); + } // OG Notify emulator is being closed, and cannot accept events anymore g_accept_events = 0; @@ -1750,7 +1766,7 @@ run_prog() engine.fcycles = prerun_fcycles; fcycles_stop = (g_event_start.next->dcycs - g_last_vbl_dcycs) + 0.001; - if(g_stepping) { + if(g_stepping || g_dbg_step < 0) { fcycles_stop = prerun_fcycles; } g_fcycles_stop = fcycles_stop; @@ -1809,6 +1825,13 @@ run_prog() this_event = g_event_start.next; while(dcycs >= this_event->dcycs) { + if(halt_sim != 0 && halt_sim != HALT_EVENT) { + break; + } + if(g_stepping || g_dbg_step != 0) { + printf("HIT STEPPING BREAK!\n"); + break; + } /* Pop this guy off of the queue */ g_event_start.next = this_event->next; @@ -1818,6 +1841,10 @@ run_prog() switch(type & 0xff) { case EV_60HZ: update_60hz(dcycs, now_dtime); + debug_server_poll(); + if (debug_events_waiting() > 0) { + debug_handle_event(); + } break; case EV_STOP: printf("type: EV_STOP\n"); @@ -1871,7 +1898,7 @@ run_prog() if(halt_sim != 0 && halt_sim != HALT_EVENT) { break; } - if(g_stepping) { + if(g_stepping || g_dbg_step != 0) { break; } } @@ -2327,7 +2354,6 @@ update_60hz(double dcycs, double dtime_now) g_dtime_pmhz_array[prev_vbl_index] = predicted_pmhz; g_dtime_eff_pmhz_array[prev_vbl_index] = eff_pmhz; - if(g_c041_val & C041_EN_VBL_INTS) { add_event_vbl(); } diff --git a/src/vars_osx_sdl2 b/src/vars_osx_sdl2 index 3b9f94a..1d02faa 100644 --- a/src/vars_osx_sdl2 +++ b/src/vars_osx_sdl2 @@ -13,8 +13,8 @@ CCOPTS = -O2 -Wall -fomit-frame-pointer -std=gnu99 -DHAVE_ICON -DHAVE_SDL -DTOGG # OPTIONS FOR COMPILING C++ SOURCE CPPOPTS = -O2 -DHAVE_TFE -DHAVE_SDL -DTOGGLE_STATUS -I/usr/local/include/freetype2 -I/usr/local/include/SDL2 -EXTRA_LIBS = -lSDL2 -lfreetype -OPTS = -DGSPLUS_LITTLE_ENDIAN +EXTRA_LIBS = -lSDL2 -lSDL2_image -lfreetype +OPTS = -DGSPLUS_LITTLE_ENDIAN -g -O0 SUFFIX = LDFLAGS = `sdl2-config --static-libs` -L/usr/local/lib LDOPTS = -I.