/* m68k_scp.c: 68k simulator SCP extension | |
Copyright (c) 2009-2010 Holger Veit | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the | |
Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
Holger Veit BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
Except as contained in this notice, the name of Holger Veit et al shall not be | |
used in advertising or otherwise to promote the sale, use or other dealings | |
in this Software without prior written authorization from Holger Veit et al. | |
17-Jul-10 HV Initial version | |
*/ | |
#include "sim_defs.h" | |
#include "m68k_cpu.h" | |
#include <ctype.h> | |
static t_bool symtrace = TRUE; | |
static void m68k_sim_init(void); | |
static t_stat hdump_cmd(int32 arg,char* buf); | |
static t_stat symset_cmd(int32 arg,char* buf); | |
static t_stat symclr_cmd(int32 arg,char* buf); | |
static t_stat symlist_cmd(int32 arg,char* buf); | |
static t_stat symtrace_cmd(int32 arg,char* buf); | |
static CTAB m68k_sim_cmds[] = { | |
{"STEP", &run_cmd, RU_STEP, | |
"s{tep} {n} simulate n instructions\n", NULL, &run_cmd_message }, | |
{"HEXDUMP", &hdump_cmd, 0, | |
"hex{dump} range dump memory\n" }, | |
{"SYMSET", &symset_cmd, 0, | |
"syms{et} name=value define symbolic name for disassembler/tracer\n"}, | |
{"SYMCLR",&symclr_cmd, 0, | |
"symc{lr} {-a|name} clear symbolic name / all symbolic names\n"}, | |
{"SYMLIST", &symlist_cmd, 0, | |
"syml{ist} [name] list symbol table\n"}, | |
{"SYMTRACE", &symtrace_cmd, 1, | |
"symt{race} enable symbolic tracing\n"}, | |
{"NOSYMTRACE", &symtrace_cmd, 0, | |
"nosymt{race} disable symbolic tracing\n"}, | |
{0,0,0,0} | |
}; | |
void (*sim_vm_init)(void) = &m68k_sim_init; | |
typedef struct _symhash { | |
struct _symhash* nnext; | |
struct _symhash* vnext; | |
char* name; | |
t_addr val; | |
} SYMHASH; | |
#define SYMHASHSIZE 397 | |
static SYMHASH *symbyname = 0; | |
static SYMHASH *symbyval = 0; | |
static void sym_clearall(void) | |
{ | |
int i; | |
SYMHASH *p,*n; | |
if (!symbyname) return; | |
for (i=0; i<SYMHASHSIZE; i++) { | |
p = symbyname[i].nnext; | |
while ((n = p) != 0) { | |
p = p->nnext; | |
free(n->name); | |
free(n); | |
} | |
symbyname[i].nnext = symbyval[i].vnext = 0; | |
} | |
return; | |
} | |
static void m68k_sim_init(void) | |
{ | |
int i; | |
sim_vm_cmd = m68k_sim_cmds; | |
sym_clearall(); | |
symbyname = (SYMHASH*)calloc(sizeof(SYMHASH),SYMHASHSIZE); | |
symbyval = (SYMHASH*)calloc(sizeof(SYMHASH),SYMHASHSIZE); | |
for (i=0; i<SYMHASHSIZE; i++) | |
symbyval[i].vnext = symbyname[i].nnext = 0; | |
symtrace = TRUE; | |
} | |
static int getnhash(const char* name) | |
{ | |
int i, nhash = 0; | |
for (i=0; name[i]; i++) { | |
nhash += name[i]; | |
} | |
return nhash % SYMHASHSIZE; | |
} | |
static int getvhash(t_addr val) | |
{ | |
return val % SYMHASHSIZE; | |
} | |
static t_bool sym_lookupname(const char *name,SYMHASH **n) | |
{ | |
int hash = getnhash(name); | |
SYMHASH *p = symbyname[hash].nnext; | |
while (p && strcmp(name,p->name)) p = p->nnext; | |
*n = p; | |
return p != 0; | |
} | |
static t_bool sym_lookupval(t_addr val, SYMHASH **v) | |
{ | |
int hash = getvhash(val); | |
SYMHASH *p = symbyval[hash].vnext; | |
while (p && p->val != val) p = p->vnext; | |
*v = p; | |
return p != 0; | |
} | |
static t_bool sym_enter(const char* name,t_addr val) | |
{ | |
int nhash = getnhash(name); | |
int vhash = getvhash(val); | |
SYMHASH *v, *n, *e; | |
if (sym_lookupname(name,&n) || sym_lookupval(val,&v)) return FALSE; | |
n = symbyname[nhash].nnext; | |
v = symbyval[vhash].vnext; | |
e = (SYMHASH*)malloc(sizeof(SYMHASH)); | |
e->nnext = n; | |
e->vnext = v; | |
e->name = malloc(strlen(name)+1); | |
strcpy(e->name,name); | |
e->val = val; | |
symbyname[nhash].nnext = symbyval[vhash].vnext = e; | |
return TRUE; | |
} | |
static t_bool sym_delete(const char* name) | |
{ | |
int hash = getnhash(name); | |
SYMHASH *p, *q, **n, **v; | |
n = &symbyname[hash].nnext; | |
while ((p = *n) != 0) { | |
if (!strcmp(p->name,name)) { /*found*/ | |
hash = getvhash(p->val); | |
v = &symbyval[hash].vnext; | |
while ((q = *v) != 0) { | |
if (q->val == p->val) { /*found*/ | |
*v = q->vnext; | |
break; | |
} | |
v = &(q->vnext); | |
} | |
*n = p->nnext; | |
free(p->name); | |
free(p); | |
return TRUE; | |
} | |
} | |
return FALSE; | |
} | |
static t_stat symset_cmd(int32 arg,char* buf) | |
{ | |
char *name,*vstr; | |
t_addr val; | |
if ((name = strtok(buf, "= ")) == 0) return SCPE_2FARG; | |
if ((vstr = strtok(NULL, " \t\n")) == 0) return SCPE_2FARG; | |
val = strtol(vstr, 0, 16); | |
if (!sym_enter(name, val)) | |
printf("Name or value already exists\n"); | |
return SCPE_OK; | |
} | |
static t_stat symclr_cmd(int32 arg,char* buf) | |
{ | |
char* token; | |
if (buf[0] == '-' && buf[1]=='a') { | |
sym_clearall(); | |
return SCPE_OK; | |
} else { | |
token = strtok(buf," \t\n"); | |
if (!token) return SCPE_2FARG; | |
return sym_delete(token) ? SCPE_OK : SCPE_ARG; | |
} | |
} | |
static t_stat symlist_cmd(int32 arg,char* buf) | |
{ | |
int i; | |
SYMHASH* n; | |
char *name; | |
t_bool found = FALSE; | |
name = strtok(buf," \t\n"); | |
if (name) { | |
if (sym_lookupname(name,&n)) | |
printf(" %s = 0x%08x\n",n->name,n->val); | |
else | |
printf("Unknown\n"); | |
} else { | |
for (i=0; i<SYMHASHSIZE; i++) { | |
n = symbyname[i].nnext; | |
while (n) { | |
printf(" %s = 0x%08x\n",n->name,n->val); | |
n = n->nnext; | |
found = TRUE; | |
} | |
} | |
if (!found) printf("Symbol table is empty\n"); | |
} | |
return SCPE_OK; | |
} | |
static t_stat symtrace_cmd(int32 arg,char* buf) | |
{ | |
if (!*buf) | |
symtrace = arg ? TRUE : FALSE; | |
printf("Symbolic tracing %sabled\n",symtrace ? "en" : "dis"); | |
return SCPE_OK; | |
} | |
static void putascii(uint32* buf) | |
{ | |
int i; | |
putchar('|'); | |
for (i=0; i<16; i++) { | |
if (isprint(buf[i])) putchar(buf[i]); | |
else putchar('.'); | |
} | |
putchar('|'); | |
} | |
static t_stat hdump_cmd(int32 arg, char* buf) | |
{ | |
int i; | |
t_addr low, high, base, top; | |
char *token; | |
uint32 byte[16]; | |
t_bool ascii = FALSE; | |
t_bool first = TRUE; | |
if (buf[0]=='-' && buf[1]=='a') { | |
ascii = TRUE; | |
buf += 2; | |
while (*buf && isspace(*buf)) buf++; | |
} | |
memset(byte,0,sizeof(uint32)*16); | |
token = strtok(buf,"- \t\n"); | |
if (!token) return SCPE_2FARG; | |
low = strtol(token,0,16); | |
token = strtok(NULL,"- \t\n"); | |
if (!token) return SCPE_2FARG; | |
high = strtol(token,0,16); | |
base = low - (low % 16); | |
top = (high + 15) - ((high+15) % 16); | |
for (; base<top; base++) { | |
if ((base % 16)==0) { | |
if (!first && ascii) putascii(byte); | |
printf("\n%08x: ",base); | |
first = FALSE; | |
} | |
if (base < low) printf(" "); | |
else if (base > high) printf(" "); | |
else { | |
i = base %16; | |
if (ReadPB(base,byte+i) != SCPE_OK) printf("?? "); | |
else printf("%02x ",byte[i] & 0xff); | |
} | |
} | |
if (!first && ascii) putascii(byte); | |
putchar('\n'); | |
return SCPE_OK; | |
} | |
char* m68k_getsym(t_addr val,const char* fmt, char* outbuf) | |
{ | |
SYMHASH *v; | |
if (symtrace && sym_lookupval(val,&v)) | |
return v->name; | |
else { | |
sprintf(outbuf,fmt,val); | |
return outbuf; | |
} | |
} | |