/* brk.c -- breakpointing routines for monitor
 * copyright (c) 1984  American Information Systems Corporation
 *  Dock Williams
 *  November, 1983
 *
 */

#include "monitor.h"
#include "3200config.h"
#include "vctype.h"


brkpnt(argc,argv)
    int argc;
    char *argv[];

/* brkpnt(argc,argv)
 * 	in:	argc		argument count
 *	in:	argv		argument vector
 *
 *	Main breakpoint handler for setting, clearing, and displaying
 *	breakpoints.  Syntax and examples:
 *
 *	b		A single 'b' will cause all set breakpoints to
 *			be displayed, including MMU breakpoints.
 *	-<#> b		A minus sign followed by number will clear that
 *			particularly numbered breakpoint.
 *	<address> - b	An address followed by a minus sign will clear
 *			the breakpoint set at that address.
 *	<address> <r,w,e,a,p,0|1> b    An address followed by a set of
 *			letters which specify breakpoint settings in an
 *			MMU breakpoint, will set a breakpoint in the MMU
 *			If a count is given as the third argument a count
 *			will be set for that breakpoint also.
 *	-* b		A minus sign followed by and asterisk clears all
 *			breakpoints.
 *	<address> b	An address followed by a 'b' will set a breakpoint 
 *			at that	address, if possible and not already set.
 *
 *	Functions called:
 *	conv, psym, clrbrk, setbrk, printf
 */
{
    register int b;
    register char *addr, *opt;
    register int optx, brkadr;
    int brkcnt, rval;

    /* if the first argument is an address convert it to binary */
    if ( (argc>1) && (*argv[0] != '-')) {
	if (conv(argv[0],&rval)) addr = (char *)(0x00ffffff & rval);
	else return;
    }

    switch(argc) {
    case 1:
	for(b=0; b<MAXBRK; b++) {
	    if(brktab[b].flag) {
		printf("%x:  ",b);
		if (cflags & CF_SYMP) psym(brktab[b].addr);
		else printf("%6x    ",brktab[b].addr);
		mdisasm(brktab[b].addr, &rval, 1, brktab[b].map);
		OUTCHR('\n');
	    }
	}
	if (cflags & CF_MMUBRK) {
	    printf("bpr0: %6x ",env->mmu.bpr0);
	    pbrkflgs(env->mmu.bpr0);
	    printf("  bcnt: %6x\n",env->mmu.bcnt);
	    printf("brp1: %6x ",env->mmu.bpr1);
	    pbrkflgs(env->mmu.bpr1);
	    OUTCHR('\n');
	}
	break;
    case 2:
	if (*argv[0] == '-') {
	    addr = argv[0];
	    clrbrk(0,*++addr);		/* clear breakpoint by number */
	} else setbrk(addr);		/* set a breakpoint */
	break;
    case 3:
	if(*argv[1] == '-') {		/* clear breakpoint */
	    clrbrk(addr,'s');		/* otherwise mmu type breakpoint */
	    break;
	}
    case 4:
	brkadr = (int)addr;
	optx = 1;
	rval = 3;
	if(argc == 4) {
	    if ( !conv(argv[1],&brkcnt) ) return;
	    brkadr |= BRK_C;
	    optx = 2;
	} else brkcnt = 0;
	opt = argv[optx];
	while (isalpha(*opt) || *opt == '1' || *opt == '0') {
	    switch(*opt++) {
	    case 'r':	brkadr |= BRK_R; break;
	    case 'w':	brkadr |= BRK_W; break;
	    case 'x':	brkadr |= BRK_X; break;
	    case 'p':	brkadr |= BRK_P; break;
	    case 'a':	brkadr |= BRK_A; break;
	    case '0':	rval = 0; break;
	    case '1':
		rval = 1;
		if (brkcnt) printf("bpr0 only");
		break;
	    default:
		break;
	    }
	}

#ifdef DEBUG1
printf("brk: addr=%8x cnt=%8x reg=%x\n",brkadr,brkcnt,rval); /* DBG */
#endif

	if (rval == 1) {
		cflags |= CF_MMUBRK;
		prgb1(brkadr);		/* call assembler routine */
		env->mmu.bpr1 = brkadr;
	}
	else if(rval == 0) {
		cflags |= CF_MMUBRK;
		prgb0(brkadr,brkcnt);
		env->mmu.bpr0 = brkadr;
		env->mmu.bcnt = brkcnt;
	}
	else printf("brk reg?\n");
    }
}



clrbrk(addr,number)
    char number;
    register char *addr;

/* clrbrk(addr,number)
 *	in:	addr		address at which to clear breakpoint
 *	in:	number		number of a breakpoint in breakpoint table
 *				    alternative to using address
 *
 *	If number is not a legal hex digit then addr is considered
 *	valid and a breakpoint is cleared, if it is set at the given
 *	address;  otherwise we clear the nth entry in the breakpoint 
 *	table which is specified by number
 *
 *	Functions called:
 *	ctox
 */
{
    register int b;

    if (isxdigit(number)) {		/* clear a breakpoint by its number */
	b = ctox(number);
	brktab[b].ins = 0;
	brktab[b].flag = 0;
    } else if (number == '*') {		/* clear all breakpoints */
	cflags &= ~CF_MMUBRK;
	env->mmu.bpr0 = env->mmu.bpr1 = 0;
    	for (b=0; b<MAXBRK; b++) {
	    brktab[b].ins = 0;
	    brktab[b].flag = 0;
	}
    } else for (b=0; b<MAXBRK; b++) {	/* clear breakpoint at this address */
	if( brktab[b].addr == addr) {
	    brktab[b].ins = 0;
	    brktab[b].flag = 0;
	    break;
	}
    }
}



msetbrk(baddr, maptype)
    register char *baddr;
    register int maptype;

/* msetbrk(baddr, maptype)
 *	in:	baddr		address at which to set breakpoint
 *	in:	maptype		byte specifying how to map baddr
 *
 *	Set a breakpoint at the argument address.  Check if the breakpoint
 *	is already set and also that it can be set in valid ram.
 *
 *	Functions called:
 *	printf
 */
{
    register int b;
    char fflag;

    fflag = 0;	/* an empty entry for a breakpoint has been found flag */

    /* is there already a brk at this address */
    for(b=0;b<MAXBRK;b++) {
	if ( (brktab[b].addr == baddr) && (brktab[b].map == maptype) ) {
	    if (brktab[b].flag) {
		printf("brk set\n");
		return;
	    } else {
		fflag++;	/* no, then use this one */
		break;
	    }
	}
    }
	
    /* find an empty entry is the breakpoint table */
    if (!fflag)
	for(b=0,fflag=0;b<MAXBRK;b++)
	    if( brktab[b].flag == 0) { fflag++; break; };

    /* check for legality and enter info in table if ok */
    if (fflag) {
	brktab[b].ins = mrdbyte(baddr, maptype);
#ifdef DB32
	mwrtmbyte(baddr, maptype, BRKCODE);	/* can we set this breakpoint */
	if(mrdbyte(baddr, maptype) != BRKCODE) {
	    printf("brk not set\n");
	    return;
	}
	mwrtmbyte(baddr, maptype, brktab[b].ins);	/* reset the instr */
#endif
	brktab[b].addr = baddr;
	brktab[b].map = maptype;
	brktab[b].flag++;
    } else printf("full\n");
}

	
misbrkset(baddr, maptype)
    register char *baddr;
    register int maptype;

/* misbrkset (baddr, maptype)
 *	in:	baddr		address to check for a breakpoint set
 *	in:	maptype		mapping flag
 *
 *  a 'boolean' function that returns a one if a brkpoint is set at the
 *  argument address and a zero otherwise
 */
{
    register int b;

    for(b=0; b<MAXBRK; b++)
	if ( (brktab[b].addr == baddr) && (brktab[b].map == maptype) &&
							(brktab[b].flag) ) {
	    return(1);
	}
    return(0);
}


brkexec()

/* brkexec()
 * 
 * set all breakpoints in the breakpoint table
 * this routine is called in returning from the debugger 
 * and also just prior to doing an xcall debugger command
 */
{
    register int b;

    for(b=0; b<MAXBRK; b++)
	if(brktab[b].flag) 
	    mwrtmbyte (brktab[b].addr, brktab[b].map, BRKCODE);
}




brkinit()

/* brkinit()
 * 
 *  replace all set breakpoints with their previous instruction byte
 *  this function is called every time the monitor is entered via
 *  trap
 */
{
    register int b;

    for(b=0; b<MAXBRK; b++)
	if(brktab[b].flag) 
	    mwrtmbyte (brktab[b].addr, brktab[b].map, brktab[b].ins);
}




    static 
pbrkflgs(bpr)
    register int bpr;

/* pbrkflgs(bpr)
 *	in:	bpr		breakpoint register
 *
 *	Print an interpretation of the flags set in the given
 *	breakpoint register
 */
{
    OUTCHR('(');
    if(bpr & BRK_A) printf(" AS");
    if(bpr & BRK_P) printf(" VP");
    if(bpr & BRK_X) printf(" BE");
    if(bpr & BRK_R) printf(" BR");
    if(bpr & BRK_W) printf(" BW");
    if(bpr & BRK_C) printf(" CE");
    printf(" )");
}

