Debugger - GSBug.Templates support.

address\temp TemplateName to print structured memory.

Also, registers now need a % prefix so as not to interfere with other commands.
This commit is contained in:
Kelvin Sherlock 2019-02-14 21:37:03 -05:00
parent 2f05a766b0
commit 5cb4f63993
4 changed files with 3005 additions and 65 deletions

View File

@ -78,6 +78,12 @@ add_custom_command(
MAIN_DEPENDENCY debug_shell.re2c
)
add_custom_command(
OUTPUT debug_template.c
COMMAND re2c -W -o ${CMAKE_CURRENT_BINARY_DIR}/debug_template.c "${CMAKE_CURRENT_SOURCE_DIR}/debug_template.re2c"
MAIN_DEPENDENCY debug_template.re2c
)
add_executable(to_pro to_pro.c)
add_executable(partls partls.c)
@ -124,6 +130,7 @@ add_executable(GSplus WIN32 MACOSX_BUNDLE
disasm.c
debug_shell.c
debug_template.c
$<$<BOOL:${WITH_DEBUGGER}>:debug.c>
$<$<BOOL:${WITH_HOST_FST}>:${host_fst_code}>
@ -132,6 +139,7 @@ add_executable(GSplus WIN32 MACOSX_BUNDLE
$<$<BOOL:${WIN32}>:win32.rc>
$<$<BOOL:${APPLE}>:assets/gsp_icon.icns>
$<$<BOOL:${APPLE}>:assets/GSBug.Templates>
$<$<BOOL:${APPLE}>:fix_mac_menu.m>
)
@ -140,8 +148,13 @@ SET_SOURCE_FILES_PROPERTIES(
assets/gsp_icon.icns
PROPERTIES
MACOSX_PACKAGE_LOCATION Resources
)
)
SET_SOURCE_FILES_PROPERTIES(
assets/GSBug.Templates
PROPERTIES
MACOSX_PACKAGE_LOCATION Resources
)
if(APPLE)

2549
src/assets/GSBug.Templates Normal file

File diff suppressed because it is too large Load Diff

View File

@ -42,6 +42,63 @@ static word32 g_prev_address = 0;
static word32 g_prev_stack_address = 0;
static word32 g_prev_stack_bank = 0;
static int g_templates_loaded = 0;
extern void debug_load_template(const char *path);
extern word32 debug_apply_template(word32 address, const char *name);
#if defined(__APPLE__)
#include <mach-o/dyld.h>
static char *get_resource_path(const char *leaf) {
uint32_t size = 0;
char *buffer = 0;
int ok;
char *cp;
int l;
l = strlen(leaf);
ok = _NSGetExecutablePath(NULL, &size);
size += l + 1 + sizeof("Resources/");
buffer = malloc(size);
if (!buffer) return NULL;
/*
* expect .../GSPlus.app/Contents/MacOS/GSPlus
* drop
*/
ok = _NSGetExecutablePath(buffer, &size);
if (ok < 0) {
free(buffer);
return NULL;
}
cp = strrchr(buffer, '/');
if (!cp) { free(buffer); return NULL; }
*cp = 0;
cp = strrchr(buffer, '/');
if (!cp) { free(buffer); return NULL; }
strcpy(cp + 1, "Resources/");
strcat(cp, leaf);
return buffer;
}
#elif defined(WIN32)
/* todo -- something clever like find the home directory */
#else
static char *get_resource_path(const char *leaf) {
int l;
char *cp;
static char path[] = "/usr/local/share/gsplus/";
l = strlen(leaf);
cp = malloc(l + sizeof(path) + 1);
if (!cp) return NULL;
strcpy(cp, path);
strcat(cp, leaf);
return cp;
}
#endif
/*
* todo
* - tool break support
@ -380,7 +437,8 @@ static word32 to_hex(const char *iter, const char *end) {
/*!re2c
re2c:define:YYCTYPE = char;
re2c:yyfill:enable = 0;
end = "\x00";
eol = "\x00";
ws = [ \t];
x = [A-Fa-f0-9];
*/
@ -394,23 +452,23 @@ static int do_assign(const char *cp, int reg) {
/*!re2c
* { return -1; }
x{6} end {
x{6} eol {
addr = to_hex(cp, cp + 6);
has_bank = 1;
goto next;
}
x{2} "/" x{4} end {
x{2} "/" x{4} eol {
addr = to_hex(cp, cp + 2) << 16;
addr |= to_hex(cp + 3, cp + 7);
has_bank = 1;
goto next;
}
x{4} end {
x{4} eol {
addr = to_hex(cp, cp + 4);
has_bank = 0;
goto next;
}
"%"[01]{2} end {
"%"[01]{2} eol {
if (reg != REG_MX) return -1;
if (cp[1] == '1') addr |= 0x20;
if (cp[2] == '1') addr |= 0x10;
@ -483,8 +541,8 @@ static word32 do_mem_assign(word32 addr, const char *cp) {
for(;;) {
const char *start = YYCURSOR;
/*!re2c
end { return addr; }
" " { continue; }
eol { return addr; }
ws { continue; }
* {
fputs("Invalid data\n", stderr);
return addr;
@ -672,68 +730,78 @@ static int parse_command(const char *cp) {
/*!re2c
_ = (" " | "\x00");
_ = (ws | eol);
* { --YYCURSOR; if (cp == YYCURSOR) goto command; return -1; }
"a=" { return do_assign(YYCURSOR, REG_A); }
"x=" { return do_assign(YYCURSOR, REG_X); }
"y=" { return do_assign(YYCURSOR, REG_Y); }
"d=" { return do_assign(YYCURSOR, REG_D); }
"e=" { return do_assign(YYCURSOR, REG_E); }
"s=" { return do_assign(YYCURSOR, REG_S); }
"k=" { return do_assign(YYCURSOR, REG_K); }
"b=" { return do_assign(YYCURSOR, REG_B); }
"p=" { return do_assign(YYCURSOR, REG_P); }
"pc=" { return do_assign(YYCURSOR, REG_PC); }
"mx=" { return do_assign(YYCURSOR, REG_MX); }
"bp" eol { show_bp('B'); return 0; }
"mp" eol { show_bp('M'); return 0; }
"bp" end { show_bp('B'); return 0; }
"mp" end { show_bp('M'); return 0; }
"*" eol { show_regs(); return 0; }
"*" end { show_regs(); return 0; }
("q" | "quit" | "exit" | "bye") eol { return 'q'; }
"reset" eol { do_reset(); return 0; }
("help" | "?") eol { do_help(); return 0; }
("q" | "quit" | "exit") end { return 'q'; }
"reset" end { do_reset(); return 0; }
("help" | "?") end { do_help(); return 0; }
// register stuff
"pc" {
"%pc++" eol {
/* todo */
return 0;
}
"%a=" { return do_assign(YYCURSOR, REG_A); }
"%x=" { return do_assign(YYCURSOR, REG_X); }
"%y=" { return do_assign(YYCURSOR, REG_Y); }
"%d=" { return do_assign(YYCURSOR, REG_D); }
"%e=" { return do_assign(YYCURSOR, REG_E); }
"5s=" { return do_assign(YYCURSOR, REG_S); }
"%k=" { return do_assign(YYCURSOR, REG_K); }
"%b=" { return do_assign(YYCURSOR, REG_B); }
"%p=" { return do_assign(YYCURSOR, REG_P); }
"%pc=" { return do_assign(YYCURSOR, REG_PC); }
"%mx=" { return do_assign(YYCURSOR, REG_MX); }
"%pc" {
addr = engine.kpc;
has_addr = 1;
has_bank = 1;
goto indir;
}
"a" {
"%a" {
addr = engine.acc;
has_addr = 1;
has_bank = 0;
goto indir;
}
"x" {
"%x" {
addr = engine.xreg;
has_addr = 1;
has_bank = 0;
goto indir;
}
"y" {
"%y" {
addr = engine.yreg;
has_addr = 1;
has_bank = 0;
goto indir;
}
"s" {
"%s" {
addr = engine.stack;
has_addr = 1;
has_bank = 1;
goto indir;
}
"d" {
"%d" {
addr = engine.dbank;
has_addr = 1;
has_bank = 1;
goto indir;
}
x{6} {
addr = to_hex(cp, cp + 6);
has_bank = 1;
@ -780,48 +848,33 @@ indir:
}
command:
if (!has_bank) {
addr |= (g_prev_address & 0xff0000);
}
if (!has_addr) {
addr = g_prev_address;
}
cp = YYCURSOR;
/*!re2c
* { return -1; }
":" {
if (!has_addr) {
addr = g_prev_address;
has_bank = 1;
}
if (!has_bank) addr |= (g_prev_address & 0xff0000);
g_prev_address = do_mem_assign(addr, YYCURSOR);
return 0;
}
(";l" | "l") end {
(";l" | "l") eol {
int psr = engine.psr;
if (!has_addr) {
addr = g_prev_address;
if (!addr) {
addr = engine.kpc;
has_bank = 1;
}
}
if (!has_bank) {
addr |= (engine.kpc & 0xff0000);
}
g_prev_address = do_list(addr, &psr, 20);
return 0;
}
";h" end {
if (!has_addr) {
addr = g_prev_address;
has_bank = 1;
}
if (!has_bank) {
addr |= (engine.dbank << 16);
}
";h" eol {
g_prev_address = do_hexdump(addr, 20);
return 0;
}
";s" end {
";s" eol {
/* print the stack */
word32 bank;
if (!has_addr) {
@ -836,44 +889,47 @@ command:
return 0;
}
"g" end {
"g" eol {
/* GO! */
if (has_addr) {
if (!has_bank) addr |= (engine.kpc & 0xff0000);
engine.kpc = addr;
}
g_stepping = 0;
return 1;
}
"s" end {
"s" eol {
/* step */
if (has_addr) {
if (!has_bank) addr |= (engine.kpc & 0xff0000);
engine.kpc = addr;
}
g_stepping = 1;
return 1;
}
";bp" [+-]? end {
";bp" [+-]? eol {
char plus = cp[3];
if (!has_addr && plus == 0) { show_bp('B'); return 0; }
if (!has_addr) return -1;
if (!has_bank) addr |= (engine.kpc & 0xff0000);
if (plus == '-') delete_bp('B', addr);
else set_bp('B', addr);
return 0;
}
";mp" [+-]? end {
";mp" [+-]? eol {
char plus = cp[3];
if (!has_addr && plus == 0) { show_bp('M'); return 0; }
if (!has_addr) return -1;
if (!has_bank) addr |= (engine.kpc & 0xff0000);
if (plus == '-') delete_bp('M', addr);
else set_bp('M', addr);
return 0;
}
"\\temp" / (ws|eol) {
/* apply a gsbug template to memory */
/* address\temp name */
while (isspace(*YYCURSOR)) ++YYCURSOR;
g_prev_address = debug_apply_template(addr, YYCURSOR);
return 0;
}
*/
}
@ -892,6 +948,15 @@ int debug_shell(int code) {
int psr = engine.psr;
if (!g_templates_loaded) {
char *path;
g_templates_loaded = 1;
path = get_resource_path("GSBug.Templates");
debug_load_template(path);
free(path);
}
/* todo -- only clear IF exit pc == entry pc ? */
if (code == RET_BP) {
@ -932,6 +997,7 @@ int debug_shell(int code) {
}
static void do_sig_intr(int sig) {
/* todo -- raise() if halt already pending */
set_halt(4);
}

312
src/debug_template.re2c Normal file
View File

@ -0,0 +1,312 @@
/*
*
* Load and parse GSBug templates and Nifty List Data.
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "defc.h"
/*!re2c
re2c:define:YYCTYPE = char;
re2c:define:YYCURSOR = cp;
re2c:yyfill:enable = 0;
eol = "\x00";
ws = [ \t\r\n];
*/
/* format
_START name
name [type [size]] *
_END
*/
struct field {
char *name;
int type;
int count;
};
struct record {
char *name;
int total_size; /* total size */
int field_count; /* # of fields */
int field_alloc;
struct field *fields;
};
static struct record *records = NULL;
static int record_count = 0;
static int record_alloc = 0;
static int record_compare(const void *a, const void *b) {
const struct record *aa = (const struct record *)a;
const struct record *bb = (const struct record *)b;
int rv;
rv = strcasecmp(aa->name, bb->name);
//if (rv == 0) return aa->total_size - bb->total_size;
return rv;
}
/* 192 records in the standard file... */
static void add_record(struct record *r) {
if (record_count == record_alloc) {
size_t size = (record_alloc + 256) * sizeof(struct record);
void *tmp = realloc(records, size);
if (!tmp) return; /* oops */
records = tmp;
record_alloc += 256;
}
records[record_count++] = *r;
}
static void free_record(struct record *r) {
int i;
for (i = 0; i < r->field_count; ++i) {
free(r->fields[i].name);
}
free(r->name);
free(r->fields);
}
/* average ~14 fields per record */
static void add_field(struct record *r, struct field *f) {
if (r->field_count == r->field_alloc) {
size_t size = (r->field_alloc + 32) * sizeof(struct field);
void *tmp = realloc(r->fields, size);
if (!tmp) return; /* oops */
r->fields = tmp;
r->field_alloc += 32;
}
r->fields[r->field_count++] = *f;
r->total_size += f->type * f->count;
}
/* 0 - no field, 1 - ok, -1 - warning */
static int parse_field(const char *cp, struct field *f) {
const char *start;
const char *end;
const char *YYMARKER = NULL;
int type = 0;
int count = 0;
start = cp;
for(;;) {
/*!re2c
eol | ws { --cp; break; }
* { continue; }
*/
}
end = cp;
if (start == end) return 0;
f->name = strndup(start, end - start);
f->type = 0;
f->count = 0;
while (isspace(*cp)) ++cp;
/* optional type */
/*!re2c
"BYTE" / (ws | eol) { type = 1; goto next; }
"WORD" / (ws | eol) { type = 2; goto next; }
"LONG" / (ws | eol) { type = 4; goto next; }
"" { type = 0; goto next; }
*/
next:
while (isspace(*cp)) ++cp;
if (type) {
count = 0;
while(isdigit(*cp)) {
count = count * 10 + *cp - '0';
++cp;
}
if (!count) count = 1;
}
while (isspace(*cp)) ++cp;
if (*cp != 0) {
return -1;
}
f->type = type;
f->count = count;
return 1;
}
void debug_load_template(const char *path) {
FILE *f;
char buffer[1024];
int ok;
int in_struct = 0;
struct record record;
struct field field;
unsigned line = 0;
const char *YYMARKER = NULL;
f = fopen(path, "r");
if (!f) {
fprintf(stderr, "Unable to load %s: %s\n", path, strerror(errno));
return;
}
for (line = 1;;++line) {
const char *start = NULL;
const char *end = NULL;
const char *cp = fgets(buffer, sizeof(buffer), f);
if (!cp) break;
/*!re2c
"" { goto _field; }
";" { continue; }
ws* eol { continue; }
"_END" / (ws | eol) { goto _end; }
"_START" / (ws | eol) { goto _start; }
*/
_field:
if (!in_struct) {
/* warn and create fake struct */
fprintf(stderr, "%s:%d: Missing _START.\n", path, line);
memset(&record, 0, sizeof(record));
in_struct = -1;
}
memset(&field, 0, sizeof(field));
ok = parse_field(cp, &field);
if (ok == 0) continue;
if (ok < 1) {
/* warn but add */
fprintf(stderr, "%s:%d: Bad field line: %s\n", path, line, buffer);
}
add_field(&record, &field);
continue;
_start:
if (in_struct) {
/* warn and close it */
if (in_struct > 0) add_record(&record);
if (in_struct < 0) free_record(&record);
}
memset(&record, 0, sizeof(record));
while (isspace(*cp)) ++cp;
start = cp;
for(;;) {
/*!re2c
eol | ws { --cp; break; }
* { continue; }
*/
}
end = cp;
if (start == end) {
/* warning ... */
fprintf(stderr, "%s:%d: _START missing name.\n", path, line);
in_struct = -1;
} else {
in_struct = 1;
record.name = strndup(start, end - start);
}
while (isspace(*cp)) ++cp;
if (*cp != 0) {
/* warning */
fprintf(stderr, "%s:%d: Bad _START line: %s\n", path, line, buffer);
}
continue;
_end:
while (isspace(*cp)) ++cp;
if (*cp != 0) {
/* warning ... */
fprintf(stderr, "%s:%d: Bad _END line: %s\n", path, line, buffer);
}
if (in_struct) {
if (in_struct > 0) add_record(&record);
if (in_struct < 0) free_record(&record);
} else {
/* warning */
fprintf(stderr, "%s:%d: _END without _START.\n", path, line);
}
in_struct = 0;
memset(&record, 0, sizeof(record));
continue;
}
if (in_struct) {
/* warn & close */
fprintf(stderr, "%s:%d: Missing _END.\n", path, line);
if (in_struct > 0) add_record(&record);
if (in_struct < 0) free_record(&record);
}
fclose(f);
qsort(records, record_count, sizeof(struct record), record_compare);
}
static int record_search(const void *a, const void *b) {
const struct record *r = b;
return strcasecmp(a, r->name);
}
word32 debug_apply_template(word32 address, const char *name) {
/* 1 - lookup template */
struct record *r;
int i, j;
r = bsearch(name, records, record_count, sizeof(struct record), record_search);
if (r == NULL) {
fprintf(stderr, "Invalid template: %s\n", name);
return address;
}
if (r->total_size == 0) {
/* just print the fields */
for (i = 0; i < r->field_count; ++i) {
fputs(r->fields[i].name, stdout);
fputc('\n', stdout);
}
fputc('\n', stdout);
return address;
}
for (i = 0; i < r->field_count; ++i) {
struct field *f = &r->fields[i];
word32 value;
unsigned type = f->type;
printf("%-16s", f->name);
for (j = 0; j < f->count; ++j) {
switch(type) {
case 1:
value = get_memory_c(address, 0);
address += 1;
printf("%02x ", value);
break;
case 2:
value = get_memory16_c(address, 0);
address += 2;
printf("%04x ", value);
break;
case 4:
value = get_memory16_c(address, 0);
address += 4;
printf("%08x ", value);
break;
}
address &= 0xffffff;
}
fputc('\n', stdout);
}
fputc('\n', stdout);
return address;
}