/* Copyright (c) 1990 by Sozobon, Limited.  Authors: Johann Ruegg, Don Dugger
 *
 * Permission is granted to anyone to use this software for any purpose
 * on any computer system, and to redistribute it freely, with the
 * following restrictions:
 * 1) No charge may be made other than reasonable charges for reproduction.
 * 2) Modified versions must be clearly marked as such.
 * 3) The authors are not responsible for any harmful consequences
 *    of using this software, even if they result from defects in it.
 *
 *	pcs.c
 */
/*
 * Modifications:
 *   - cmdcol() changed for a cooperation with stepping commands
 *   - added function getrequs() which handles turning on/off
 *     execution on breakpoints and stepping commands
 *   - added function findval() which returns values of read-only
 *     internal variables
 *
 *     Anthony Howe, Michal Jaegermann, April 1990
 *
 *   - prnstack now uses Unwind function from stepping.c to find
 *     a chain of return addresses.  (MJ - November 1990)
 */
#include <setjmp.h>
#include <ctype.h>
#include "adb.h"

#define IN_PCS
#include "lang.h"

#ifndef NULL
#define NULL	0L
#endif

unsigned long   esp;
struct basepg  *bpage;
jmp_buf         cont_buf;
static int      got_args = 0;

extern long     dot;
extern long     olddot;
extern int      dotset;
extern int      dotoff;
extern int      lastc;
extern int      print_regs;
extern int      click;
extern struct regs regs[];
extern next_step next_list[];
extern next_step jump_list[];

prstack (argsflg)
{
    int             first, at_sp;
    long            pc, fp, sp, ap;
    long            npc, nfp;
    long            getn ();
    short          *Unwind();
    struct basepg  *bp = bpage;

    pc = *(regs[PC].value);
    fp = *(regs[FP].value);
    sp = *(regs[SP].value);
    at_sp = first = 1;

    while (pc) {
	prtf ("%A", pc);
	npc = (long) Unwind (&at_sp, pc, sp, fp);
				 /* this may modify value of at_sp */
	if (first && at_sp) {
	    nfp = fp;
	    ap = sp + 4;
	    at_sp = 0;		    /* get the next return address from fp */
	}
	else {
	    nfp = getn (fp, 4);
	    ap = fp + 8;
	}
	if (argsflg && npc)
	    funargs (npc, ap, nfp);
	putchr ('\n');

	if (nfp < (long) bp->p_bbase + bp->p_blen ||
	    nfp > (long) bp->p_hitpa ||
	    nfp <= sp || nfp < fp ||
	    (nfp == fp && !first))
	    break;
	pc = npc;
	fp = nfp;
	first = 0;
    }
}

funargs (retpc, argp, limit)
    long            retpc, argp, limit;
{
    int             i, n;
    unsigned        w;

    n = numargs (retpc);

    if (n == 0) {		/* optimized out maybe */
/*		prtf("(? at %I)", argp); */
	prtf (M1, argp);
	return;
    }

    putchr ('(');
    for (i = 0; i < n; i++, argp += 2) {
	w = getn (argp, 2);
	prt4x (w);
	if (argp + 2 >= limit)
	    break;
	if ((i + 1) < n)
	    putchr (',');
    }
    putchr (')');
}

prt4x (w)
    unsigned        w;
{
    int             i, j, k;

    k = 12;
    for (k = 12; k >= 0; k -= 4) {
	j = w >> k;
	j &= 0xf;
	putchr ("0123456789abcdef"[j]);
    }
}

numargs (retpc)
    long            retpc;
{
    int             ins, n;
    long            getn ();

    ins = getn (retpc, 2);
    if ((ins & 0xf1ff) == 0x504f ||
	(ins & 0xf1ff) == 0x508f) {	/* addq.[wl] #n,A7 */
	n = (ins >> 9) & 7;
	if (n == 0)
	    n = 8;
    }
    else if (ins == 0xdefc) {	/* adda.w #n,A7 */
	n = getn (retpc + 2, 2);
    }
    else if (ins == 0xdffc) {	/* adda.l #n,A7 */
	n = getn (retpc + 2, 4);
    }
    else
	n = 0;

    if (n < 0)
	n = 0;
    return n / 2;
}

long
atbranch (loc)
    long            loc;
{
    int             ins, i;

    ins = getn (loc, 2);
    if ((ins & 0xff00) == 0x6000) {
	i = ins & 0xff;
	if (i == 0)
	    i = getn (loc + 2, 2);
	return loc + 2 + i;
    }
    return 0;
}

atlink (loc)
    long            loc;
{
    int             ins;

    ins = getn (loc, 2);
    return (ins == 0x4e56);	/* link a6,#N */
}

atrts (loc)
    long            loc;
{
    int             ins;

    ins = getn (loc, 2);
    return (ins == 0x4e75);
}

prbasepg ()
{
    int             n, i;
    unsigned char   c;
    struct basepg  *bp;

    if (dotset)
	bp = (struct basepg *) dot;
    else
	bp = bpage;

/*	prtf("base page at %I", bp); */
    prtf (M2, bp);
/*	prt("low tpa ");	*/
    prt (M3);
    prtn (bp->p_lowtpa, 10);
/*	prt("  hi tpa  ");	*/
    prt (M4);
    prtn (bp->p_hitpa, 10);
/*	prt("\ntext at ");	*/
    prt (M5);
    prtn (bp->p_tbase, 10);
/*	prt("  size    ");	*/
    prt (M6);
    prtn (bp->p_tlen, 10);
/*	prt("\ndata at ");	*/
    prt (M7);
    prtn (bp->p_dbase, 10);
/*	prt("  size    ");	*/
    prt (M6);
    prtn (bp->p_dlen, 10);
/*	prt("\nbss at  ");	*/
    prt (M8);
    prtn (bp->p_bbase, 10);
/*	prt("  size    ");	*/
    prt (M6);
    prtn (bp->p_blen, 10);
/*	prt("\nenv ptr ");	*/
    prt (M9);
    prtn (bp->p_env, 10);
/*	prt("  parent  ");	*/
    prt (M10);
    prtn (bp->p_parent, 10);

    prtf ("\nargs: ");
    n = bp->p_cmdlin[0];
    for (i = 0; i < n;) {
	c = bp->p_cmdlin[++i];
	if (c < ' ') {
	    putchr ('^');
	    c += 'A';
	}
	putchr (c);
    }
    putchr ('\n');
}

loadpcs ()
{
    int           **ip;
    char            parms[80], *envp;
    extern struct basepg *gemdos ();
    extern struct file binary;
    extern struct basepg *_base;
    extern unsigned long ossp;

    parms[0] = '\0';
    envp = _base->p_env;
    if ((bpage = gemdos (0x4b, 3, binary.name, parms, envp)) < 0) {
/*		prtf("can't load %s\n", binary.name);	*/
	prtf (M11, binary.name);
	return -1;
    }
    ip = (int **) (bpage->p_hitpa);
    *--ip = (int *) bpage;
    --ip;
    *(regs[SP].value) = (unsigned long) ip;
    *(regs[PC].value) = dot = olddot = (unsigned long) bpage->p_tbase;
    *(regs[XSP].value) = ossp;
#ifdef OLD
    bpage->p_parent = _base;
#else
    bpage->p_parent = 0;
#endif
    return 0;
}

void
cmdcol (c, fmt, get)
    int             c;
    char           *fmt;
    long            (*get) ();
{
    extern int      MakeReq ();
    int             kind, tmp;

    if ('b' == c) {		/* set breakpoint */
	SetBpt (dot);
    }
    else if ('d' == c) {	/* clear breakpoint */
	ClrBpt (dot);
    }
    else {			/* tracing */
	if (dotset)
	    *regs[PC].value = dot;
	else
	    dot = *regs[PC].value;

	c = ((print_regs = isupper (c)) ? tolower (c) : c);
	if ('s' == c)
	    kind = CM_STEP;
	else if ('n' == c || 'j' == c)
	    kind = CM_NEXT;
	else if ('f' == c)
	    kind = CM_FINISH;
	else {
	    kind = CM_CONT;	/* we hope */
	    if (click)		/* command line started with :: */
		c = 0;		/* force error */
	}

	if (CM_CONT == kind || 0 == MakeReq (kind)) {
	    switch (c) {
	    case 's':
		SingleStep (dot, CM_STEP);
		break;
	    case 'c':
		getargs ();
		if (setjmp (cont_buf)) {
		    dot = *regs[PC].value;
		}
		FullStep (dot, (short *) NULL, CM_CONT);
		break;
	    case 'n':
		NextStep (dot, next_list);
		break;
	    case 'j':
		NextStep (dot, jump_list);
		break;
	    case 'f':
		FuncStep (dot);
		break;
	    default:
		prt (UNKNOWN_CMD);
	    }			/* switch */
	}			/* if (click) */
    }				/* if ('b' == c) */
    click = 0;			/* next time around execute */
    return;
}				/* cmdcol */

int
getrequs (buf)
    char           *buf;
/*
 *   If 'click' is off and if on a current position, and after all white space
 *   was skipped, there is a character other then '\n', then copy all remaining
 *   characters on a line into a supplied buffer buf. Terminate with '\n\0'.
 *   Return ON if something was copied and BLANK otherwise.
 *   If click is ON return one of the status comands depending on what follows.
 *   Skip to the next delimiter and push '\n'.
 */
{
    register int    c;
    register char  *pos = buf;
    int             status;
    extern int      getchr (), nb ();

    if (0 == click) {
	if ('\n' != PEEKC) {
	    do {
		*pos++ = c = getchr (1);
	    } while ('\n' != c);
	    *pos++ = '\0';
	    status = NEW;
	}
	else	/* '\n' is pushed */
	    return BLANK;
    }
    else {			/* we are changing status of attached
				 * commands - ignore buffer */
	switch (c = nb ()) {
	case '+':
	    status = ON;
	    break;
	case '-':
	    status = SILENT;
	    break;
	case '_':
	    status = DOWN;
	    break;
	case '`':
	    status = REVERT;
	    break;
	default:
	    status = IGNORE;
	}
	while (getchr (0) != '\n') /* skip to the next delimiter */
	    ;
    }
    PUSHC ('\n');
    return (status);
}				/* getrequs */

getargs ()
{
    char            c, *cp;
    int             n;

    c = nb ();
    if (c == '\n')
	return;

    if (got_args)
	return;
    got_args++;

    n = 1;
    cp = &bpage->p_cmdlin[n];
    *cp++ = c;
    while ((c = getchr (1)) != '\n') {
	*cp++ = c;
	n++;
	if (n >= 0x7f)
	    break;
    }
    bpage->p_cmdlin[0] = n;
    *cp = 0;
    PUSHC ('\n');
}

void
copyargs (argc, argv)
    int             argc;
    char          **argv;
/*
 * If argc > 0 copy command line arguments into a base page of
 * process to debug. Terminating zero not included in count.
 */
{
    char            c, *cp;
    int             n;

    if (0 == argc) {
	bpage->p_cmdlin[0] = 0;
	return;
    }
    got_args++;

    n = 0;
    cp = &bpage->p_cmdlin[1];
    for (;;) {
	if (n >= 0x7e)
	    break;
	while (0 != (c = *(*argv)++)) {
	    *cp++ = c;
	    n++;
	    if (n >= 0x7e)
		goto out;
	}
	if (--argc == 0)
	    break;
	*cp++ = ' ';
	n++;
    }
out:
    *cp = 0;
    bpage->p_cmdlin[0] = n;
}


#ifdef GONER
cleanup (fp)
    struct file    *fp;
{

    ptrace (KILL_PID, fp->pid, 0, 0);
    return;
}

#endif

prbpt (pc)
    long            pc;
{
    dot = pc;
    dotoff = 0;
    prtad (dot + dotoff);
    putchr (':');
    puti ();
    return;
}

long
findval (buf, err_p)
    char           *buf;
    int            *err_p;
/*
 * If a string in buf is "l", "b", "t" or "d" return value of
 * text segment base, data segment base, text size and data size
 * respectively.  Otherwise set error indicator and return 0.
 *
 * Names are chosen to avoid conflicts with a Un*x adb. Names
 * "b", "t", "d" and some other are already taken.
 */
{
    struct basepg  *bp = bpage;

    if (0 == (*err_p = buf[1])) {
	switch (buf[0]) {
	case 'l':
	    return (bp->p_tbase);
	case 'b':
	    return (bp->p_dbase);
	case 'd':
	    return (bp->p_dlen);
	case 't':
	    return (bp->p_tlen);
	}
    }
    *err_p = 1;
    return 0;
}