mirror of
https://github.com/simh/simh.git
synced 2026-04-18 00:48:13 +00:00
Beta Simulators (PDQ-3 and SAGE) from Holger Veit
This commit is contained in:
441
PDQ-3/pdq3_debug.c
Normal file
441
PDQ-3/pdq3_debug.c
Normal file
@@ -0,0 +1,441 @@
|
||||
/* pdq3_debug.c: PDQ3 debug helper
|
||||
|
||||
Work derived from Copyright (c) 2004-2012, Robert M. Supnik
|
||||
Copyright (c) 2013 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
|
||||
ROBERT M SUPNIK 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 names of Robert M Supnik and Holger Veit
|
||||
shall not be used in advertising or otherwise to promote the sale, use or other dealings
|
||||
in this Software without prior written authorization from Robert M Supnik and Holger Veit.
|
||||
|
||||
20130421 hv initial version
|
||||
20130928 hv fix problem with callstack when S_Start_P patches MSCW
|
||||
20131012 hv view calltree returned incorrect segment of caller
|
||||
*/
|
||||
#include "pdq3_defs.h"
|
||||
|
||||
static uint8 *opdebug = NULL;
|
||||
|
||||
static void dbg_opdbgcreate() {
|
||||
int i;
|
||||
FILE *fd = fopen(DEBUG_OPDBGFILE, "w");
|
||||
if (fd==NULL) {
|
||||
fprintf(stderr,"Cannot create %s\n", DEBUG_OPDBGFILE);
|
||||
exit(1);
|
||||
}
|
||||
for (i=DEBUG_MINOPCODE; i<DEBUG_MAXOPCODE; i++) {
|
||||
if (DEBUG_VALIDOP(i)) {
|
||||
fprintf(fd,"%x %d ;%s\n",
|
||||
i, DEBUG_PRE|DEBUG_POST, optable[i].name);
|
||||
} else {
|
||||
fprintf(fd,"%x %d ;invalid\n",
|
||||
i, DEBUG_PRE|DEBUG_POST);
|
||||
}
|
||||
}
|
||||
fclose(fd);
|
||||
fprintf(stderr,"%s created. Adapt file manually and restart simh\n", DEBUG_OPDBGFILE);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
static void dbg_opdbginit() {
|
||||
char line[100];
|
||||
int i, f;
|
||||
FILE* fd = fopen(DEBUG_OPDBGFILE,"r");
|
||||
if (fd == NULL)
|
||||
dbg_opdbgcreate(); /* will not return */
|
||||
|
||||
if (opdebug == NULL)
|
||||
opdebug = (uint8*)calloc(DEBUG_MAXOPCODE-DEBUG_MINOPCODE,sizeof(uint8));
|
||||
|
||||
for (i=DEBUG_MINOPCODE; i<DEBUG_MAXOPCODE; i++)
|
||||
opdebug[i-DEBUG_MINOPCODE] = DEBUG_PRE|DEBUG_POST;
|
||||
while (!feof(fd)) {
|
||||
fgets(line,100,fd);
|
||||
sscanf(line,"%x %d", &i, &f);
|
||||
opdebug[i-DEBUG_MINOPCODE] = f;
|
||||
}
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
t_stat dbg_check(t_value op, uint8 flag) {
|
||||
if (opdebug[op-DEBUG_MINOPCODE] & flag) {
|
||||
if (flag & DEBUG_PRE) {
|
||||
opdebug[op-DEBUG_MINOPCODE] &= ~DEBUG_PRE;
|
||||
return STOP_DBGPRE;
|
||||
} else
|
||||
return STOP_DBGPOST;
|
||||
}
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat dbg_dump_tib(FILE *fd, uint16 base) {
|
||||
t_stat rc;
|
||||
uint16 data;
|
||||
fprintf(fd, "TIB at $%04x (CTP=$%04x, RQ=$%04x)\n",base, reg_ctp, reg_rq);
|
||||
if ((rc=ReadEx(base, OFF_WAITQ, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " WAITQ: $%04x\n",data);
|
||||
if ((rc=ReadBEx(base+OFFB_PRIOR, 0, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " PRIOR: %02x\n",data);
|
||||
if ((rc=ReadEx(base, OFF_SPLOW, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " SPLOW: $%04x\n",data);
|
||||
if ((rc=ReadEx(base, OFF_SPUPR, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " SPUPR: $%04x\n",data);
|
||||
if ((rc=ReadEx(base, OFF_SP, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " SP: $%04x\n",data);
|
||||
if ((rc=ReadEx(base, OFF_MP, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " MP: $%04x\n",data);
|
||||
if ((rc=ReadEx(base, OFF_BP, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " BP: $%04x\n",data);
|
||||
if ((rc=ReadEx(base, OFF_IPC, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " IPC: #%04x\n",data);
|
||||
if ((rc=ReadEx(base, OFF_SEGB, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " SEGB: $%04x\n",data);
|
||||
if ((rc=ReadEx(base, OFF_HANGP, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " HANGP: $%04x\n",data);
|
||||
if ((rc=ReadEx(base, OFF_IORSLT, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " IORSLT: %04x\n",data);
|
||||
if ((rc=ReadEx(base, OFF_SIBS, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " SIBS: $%04x\n",data);
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat dbg_dump_queue(FILE* fd, const char* qname, uint16 q) {
|
||||
t_stat rc;
|
||||
fprintf(fd, "dump queue %s: address=$%04x\n ",qname, q);
|
||||
while (q != NIL) {
|
||||
fprintf(fd, "$%04x->",q);
|
||||
if ((rc=ReadEx(q, OFF_WAITQ, &q)) != SCPE_OK) return rc;
|
||||
}
|
||||
fprintf(fd, "NIL\n");
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat dbg_dump_mscw(FILE* fd, uint16 base) {
|
||||
t_stat rc;
|
||||
uint16 data;
|
||||
fprintf(fd, "MSCW at $%04x\n",base);
|
||||
if ((rc=ReadEx(base, OFF_MSSTAT, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " MSSTAT: $%04x\n", data);
|
||||
if ((rc=ReadEx(base, OFF_MSDYNL, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " MSDYNL: $%04x\n", data);
|
||||
if ((rc=ReadEx(base, OFF_MSIPC, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " MSIPC: $%04x\n", data);
|
||||
if ((rc=ReadBEx(base+OFFB_MSSEG, 0, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " MSSEG: %02x\n", data);
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
void dbg_enable() {
|
||||
cpu_dev.dctrl |= (DBG_CPU_READ|DBG_CPU_WRITE|DBG_CPU_STACK);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Segment Tracking support
|
||||
*****************************************************************************/
|
||||
|
||||
static char* pdq3_segname(uint16 nameptr) {
|
||||
static char name[10];
|
||||
uint16 data;
|
||||
int i;
|
||||
for (i=0; i<8; i++) {
|
||||
ReadBEx(nameptr,i,&data);
|
||||
name[i] = data != ' ' ? data : 0;
|
||||
}
|
||||
name[8] = 0;
|
||||
return name;
|
||||
}
|
||||
|
||||
t_stat dbg_dump_seg(FILE* fd, uint16 segptr) {
|
||||
t_stat rc;
|
||||
uint16 data;
|
||||
if ((rc=ReadEx(segptr, OFF_SEGBASE, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " BASE: $%04x\n",data);
|
||||
if ((rc=ReadEx(segptr, OFF_SEGLENG, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " LENGTH: $%04x\n",data);
|
||||
if ((rc=ReadEx(segptr, OFF_SEGREFS, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " REFS: $%04x\n",data);
|
||||
if ((rc=ReadEx(segptr, OFF_SEGADDR, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " ADDR: $%04x\n",data);
|
||||
if ((rc=ReadEx(segptr, OFF_SEGUNIT, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " UNIT: $%04x\n",data);
|
||||
if ((rc=ReadEx(segptr, OFF_PREVSP, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " PREVSP: $%04x\n",data);
|
||||
fprintf(fd, " NAME: %s\n", pdq3_segname(segptr+OFF_SEGNAME));
|
||||
if ((rc=ReadEx(segptr, OFF_SEGLINK, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " LINK: $%04x\n",data);
|
||||
if ((rc=ReadEx(segptr, OFF_SEGGLOBAL, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " GLOBAL: $%04x\n",data);
|
||||
if ((rc=ReadEx(segptr, OFF_SEGINIT, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " INIT: $%04x\n",data);
|
||||
if ((rc=ReadEx(segptr, OFF_SEG13, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " entry13: $%04x\n",data);
|
||||
if ((rc=ReadEx(segptr, OFF_SEGBACK, &data)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " SELF: $%04x\n",data);
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat dbg_dump_segtbl(FILE* fd) {
|
||||
int i;
|
||||
uint16 segptr, nsegs;
|
||||
t_stat rc;
|
||||
|
||||
if (reg_ssv < 0x2030 || reg_ssv > 0xf000) {
|
||||
printf("Cannot list segments in bootloader: incomplete tables\n");
|
||||
return SCPE_NXM;
|
||||
}
|
||||
|
||||
if ((rc=Read(reg_ssv, -1, &nsegs, 0)) != SCPE_OK) return rc;
|
||||
|
||||
fprintf(fd, "Segment table: ssv=$%04x size=%d\n",reg_ssv, nsegs);
|
||||
for (i=0; i<=nsegs; i++) {
|
||||
if ((rc=ReadEx(reg_ssv, i, &segptr)) != SCPE_OK) return rc;
|
||||
fprintf(fd, " %02x %04x %s\n",i,segptr, pdq3_segname(segptr + OFF_SEGNAME));
|
||||
}
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/* segment tracking */
|
||||
typedef struct _seginfo {
|
||||
uint16 base; /* base load address */
|
||||
struct _seginfo* next;
|
||||
uint16 idx; /* index into SSV table */
|
||||
char name[10]; /* segment name */
|
||||
uint16 size;
|
||||
uint16 nproc;
|
||||
uint16 segno;
|
||||
} SEGINFO;
|
||||
|
||||
#define SEGHASHSIZE 97
|
||||
SEGINFO* seghash[SEGHASHSIZE];
|
||||
#define SEGHASHFUNC(i) (i % SEGHASHSIZE)
|
||||
|
||||
void dbg_segtrackinit() {
|
||||
int i;
|
||||
for (i=0; i<SEGHASHSIZE; i++)
|
||||
seghash[i] = NULL;
|
||||
}
|
||||
|
||||
static SEGINFO* new_seginfo(SEGINFO* next, uint16 base) {
|
||||
SEGINFO* s = (SEGINFO*)malloc(sizeof(SEGINFO));
|
||||
s->next = next;
|
||||
s->base = base;
|
||||
return s;
|
||||
}
|
||||
|
||||
static SEGINFO* find_seginfo(uint16 base, int* idx) {
|
||||
SEGINFO* s;
|
||||
*idx = SEGHASHFUNC(base);
|
||||
s = seghash[*idx];
|
||||
while (s && s->base != base) s = s->next;
|
||||
return s;
|
||||
}
|
||||
|
||||
t_stat dbg_segtrack(uint16 segbase) {
|
||||
t_stat rc;
|
||||
int idx;
|
||||
SEGINFO* s = find_seginfo(segbase, &idx);
|
||||
if (!s) {
|
||||
s = seghash[idx] = new_seginfo(seghash[idx], segbase);
|
||||
if ((rc=ReadEx(segbase, 0, &s->size)) != SCPE_OK) return rc;
|
||||
strcpy(s->name, segbase==0xf418 ? "HDT" : pdq3_segname(segbase+2));
|
||||
if ((rc=ReadBEx(segbase+s->size, 0, &s->segno)) != SCPE_OK) return rc;
|
||||
if ((rc=ReadBEx(segbase+s->size, 1, &s->nproc)) != SCPE_OK) return rc;
|
||||
// printf("Entered at %04x: %s sz=%x seg=%x np=%x\n",segbase, s->name, s->size, s->segno, s->nproc);
|
||||
}
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Name Alias Handling
|
||||
*****************************************************************************/
|
||||
|
||||
typedef struct _aliases {
|
||||
char* key;
|
||||
char* alias;
|
||||
struct _aliases* next;
|
||||
} ALIASES;
|
||||
|
||||
#define ALIASHASHSIZE 97
|
||||
static ALIASES* aliases[ALIASHASHSIZE];
|
||||
|
||||
static void dbg_initaliases() {
|
||||
int i;
|
||||
for (i=0; i<ALIASHASHSIZE; i++)
|
||||
aliases[i] = NULL;
|
||||
}
|
||||
|
||||
static int aliashash(const char* key) {
|
||||
int i, h=0;
|
||||
int len = strlen(key);
|
||||
for (i=0; i<len; i++)
|
||||
h += key[i];
|
||||
return h % ALIASHASHSIZE;
|
||||
}
|
||||
|
||||
static ALIASES* find_alias(const char* key, int* idx) {
|
||||
ALIASES* a;
|
||||
char gbuf[CBUFSIZE], gbuf2[CBUFSIZE];
|
||||
|
||||
get_glyph(key, gbuf, 0);
|
||||
*idx = aliashash(key);
|
||||
a = aliases[*idx];
|
||||
if (a) get_glyph(a->key, gbuf2, 0);
|
||||
while (a && strcmp(gbuf2,gbuf)) {
|
||||
a = a->next;
|
||||
if (a) get_glyph(a->key, gbuf2, 0);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
t_stat dbg_enteralias(const char* key, const char* value) {
|
||||
int idx;
|
||||
ALIASES* a = find_alias(key, &idx);
|
||||
if (!a) {
|
||||
a = (ALIASES*)malloc(sizeof(ALIASES));
|
||||
a->key = strdup(key);
|
||||
a->alias = strdup(value);
|
||||
a->next = aliases[idx];
|
||||
aliases[idx] = a;
|
||||
}
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat dbg_listalias(FILE* fd) {
|
||||
int i;
|
||||
ALIASES* a;
|
||||
fprintf(fd, "Name table:\n");
|
||||
for (i=0; i<ALIASHASHSIZE; i++) {
|
||||
a = aliases[i];
|
||||
while (a) {
|
||||
fprintf(fd, " Name %s = %s\n", a->key, a->alias);
|
||||
a = a->next;
|
||||
}
|
||||
}
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Procedure tracking support
|
||||
*****************************************************************************/
|
||||
|
||||
typedef struct _procinfo {
|
||||
struct _procinfo *next;
|
||||
uint16 procno;
|
||||
SEGINFO* seg;
|
||||
uint16 localsz;
|
||||
uint16 freesz;
|
||||
uint16 mscw;
|
||||
uint16 segb;
|
||||
uint16 instipc;
|
||||
uint16 ipc;
|
||||
} PROCINFO;
|
||||
|
||||
const char* find_procname(PROCINFO* p) {
|
||||
ALIASES* a;
|
||||
int dummy;
|
||||
static char buf[100];
|
||||
sprintf(buf,"%s:proc%d", p->seg->name, p->procno);
|
||||
a = find_alias(buf, &dummy);
|
||||
if (a) return a->alias;
|
||||
return buf;
|
||||
}
|
||||
|
||||
static PROCINFO* procroot = NULL;
|
||||
|
||||
static PROCINFO* new_procinfo(uint16 segbase, uint16 procno, uint16 mscw, uint16 osegb) {
|
||||
int dummy;
|
||||
uint16 procbase, procaddr;
|
||||
uint16 exitic, sz1, sz2;
|
||||
PROCINFO* p = (PROCINFO*)malloc(sizeof(PROCINFO));
|
||||
p->procno = procno;
|
||||
p->mscw = mscw;
|
||||
p->seg = find_seginfo(segbase, &dummy);
|
||||
p->segb = osegb;
|
||||
p->instipc = ADDR_OFF(PCX);
|
||||
ReadEx(mscw,OFF_MSIPC, &p->ipc);
|
||||
ReadEx(segbase, 0, &procbase);
|
||||
ReadEx(segbase+procbase-procno, 0, &procaddr);
|
||||
ReadEx(segbase+procaddr, 0, &p->localsz);
|
||||
ReadEx(segbase+procaddr-1, 0, &exitic);
|
||||
ReadBEx(segbase, exitic, &sz1);
|
||||
if (sz1==0x96) {
|
||||
ReadBEx(segbase, exitic+1, &sz1);
|
||||
if (sz1 & 0x80) {
|
||||
ReadBEx(segbase, exitic+2, &sz2);
|
||||
sz1 = ((sz1 & 0x7f)<<8) | sz2;
|
||||
}
|
||||
p->freesz = sz1;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
t_stat dbg_procenter(uint16 segbase, uint16 procno, uint16 mscw, uint16 osegb) {
|
||||
PROCINFO* p = new_procinfo(segbase, procno, mscw, osegb);
|
||||
p->next = procroot;
|
||||
procroot = p;
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat dbg_procleave() {
|
||||
t_stat rc;
|
||||
PROCINFO* p = procroot;
|
||||
uint16 ipc,pipc;
|
||||
while (p) {
|
||||
pipc = p->ipc;
|
||||
if ((rc=ReadEx(p->mscw,OFF_MSIPC, &ipc)) != SCPE_OK) return rc;
|
||||
procroot = p->next;
|
||||
free(p);
|
||||
if (pipc == ipc) break;
|
||||
p = procroot;
|
||||
}
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
t_stat dbg_calltree(FILE* fd) {
|
||||
PROCINFO* p = procroot, *lastp;
|
||||
|
||||
if (!p) {
|
||||
fprintf(fd,"Callstack is empty\n");
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
fprintf(fd,"Calltree:\nCurrently in %s at %04x:%04x\n",
|
||||
find_procname(p), reg_segb, reg_ipc);
|
||||
lastp = p;
|
||||
p = p->next;
|
||||
while (p) {
|
||||
fprintf(fd," at %04x:%04x called by %s (%04x:%04x)\n",
|
||||
lastp->segb, lastp->instipc, find_procname(p), p->segb, p->instipc);
|
||||
lastp = p;
|
||||
p = p->next;
|
||||
}
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Initialization
|
||||
*****************************************************************************/
|
||||
|
||||
t_stat dbg_init() {
|
||||
dbg_opdbginit();
|
||||
dbg_segtrackinit();
|
||||
return SCPE_OK;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user