/*
 * Copyright (c) 1992, 1993, 1996
 *	Berkeley Software Design, Inc.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Berkeley Software
 *	Design, Inc.
 *
 * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	BSDI trace.c,v 2.2 1996/04/08 19:33:07 bostic Exp
 */

#include "doscmd.h"
#include "trap.h"

extern FILE *debugf;
int tmode = 0;

static u_short *saddr;
static u_char *iaddr, ibyte;

/*
 * Before exiting to VM86 mode:
 * 1) Always set the trap flag.
 * 2) If this is a POPF or IRET instruction, set the trap flag in the saved
 *    flag state on the stack.
 * On enterint from VM86 mode:
 * 1) Restore the trap flag from our saved flag state.
 * 2) If we just finished a POPF or IRET unstruction, patch the saved flag
 *    state on the stack.
 */

int tracetype;

int
resettrace(struct sigcontext *sc)
{

	if ((sc->sc_eflags & PSL_VM) == 0)
		return (0);

	switch (tracetype) {
	case 1:
		sc->sc_eflags &= ~PSL_T;
		tracetype = 0;
		return (1);
	case 2:
		if ((u_char *)MAKEPTR(GET16(sc->sc_cs), GET16(sc->sc_eip) - 1) == iaddr)
			SET16(sc->sc_eip, GET16(sc->sc_eip) - 1);
		*iaddr = ibyte;
		tracetype = 0;
		return (1);
	case 3:
	case 4:
		sc->sc_eflags &= ~PSL_T;
		*saddr &= ~PSL_T;
		tracetype = 0;
		return (1);
	}

	return (0);
}

void
tracetrap(struct sigcontext *sc)
{
	u_char *addr;
	int n;
	char buf[100];

	if ((sc->sc_eflags & PSL_VM) == 0)
		return;

	addr = (u_char *)GETPTR(sc->sc_cs, sc->sc_eip);

	n = i386dis(GET16(sc->sc_cs), GET16(sc->sc_eip), addr, buf, 0);
	printtrace(sc, buf);

	switch (addr[0]) {
	case REPNZ:
	case REPZ:
		tracetype = 2;
		iaddr = (u_char *)MAKEPTR(GET16(sc->sc_cs), GET16(sc->sc_eip) + n);
		break;
	case PUSHF:
		tracetype = 4;
		saddr = (u_short *)MAKEPTR(GET16(sc->sc_ss), GET16(sc->sc_esp) - 2);
		break;
	case POPF:
		tracetype = 3;
		saddr = (u_short *)MAKEPTR(GET16(sc->sc_ss), GET16(sc->sc_esp) + 0);
		break;
	case IRET:
		tracetype = 3;
		saddr = (u_short *)MAKEPTR(GET16(sc->sc_ss), GET16(sc->sc_esp) + 4);
#if 0
		printf("IRET: %04x %04x %04x\n",
		    ((u_short *)GETPTR(sc->sc_ss, sc->sc_esp))[0],
		    ((u_short *)GETPTR(sc->sc_ss, sc->sc_esp))[1],
		    ((u_short *)GETPTR(sc->sc_ss, sc->sc_esp))[2]);
#endif
		break;
	case OPSIZ:
		switch (addr[1]) {
		case PUSHF:
			tracetype = 4;
			saddr = (u_short *)MAKEPTR(GET16(sc->sc_ss), GET16(sc->sc_esp) - 4);
			break;
		case POPF:
			tracetype = 3;
			saddr = (u_short *)MAKEPTR(GET16(sc->sc_ss), GET16(sc->sc_esp) + 0);
			break;
		case IRET:
			tracetype = 3;
			saddr = (u_short *)MAKEPTR(GET16(sc->sc_ss), GET16(sc->sc_esp) + 8);
			break;
		default:
			tracetype = 1;
			break;
		}
	default:
		tracetype = 1;
		break;
	}

	switch (tracetype) {
	case 1:
	case 4:
		if (sc->sc_eflags & PSL_T)
			tracetype = 0;
		else
			sc->sc_eflags |= PSL_T;
		break;
	case 2:
		if (*iaddr == TRACETRAP)
			tracetype = 0;
		else {
			ibyte = *iaddr;
			*iaddr = TRACETRAP;
		}
		break;
	case 3:
		sc->sc_eflags |= PSL_T;
		if (*saddr & PSL_T)
			tracetype = 0;
		else
			*saddr |= PSL_T;
		break;
	}
}

inline
showstate(long flags, long flag, char f)
{
    putc((flags & flag) ? f : ' ', debugf);
}

printtrace(struct sigcontext *sc, char *buf)
{

    static int first = 1;
    u_char *addr = (u_char *)GETPTR(sc->sc_cs, sc->sc_eip);
    char *bigfmt = "%04x:%04x "
#if BIG_DEBUG
	   	   "%02x %02x %02x %02x %02x %02x "
#endif
	   	   "%-30s "
           	   "%04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x ";

    if (first) {
	fprintf(debugf, "%4s:%4s "
#if BIG_DEBUG
	       ".. .. .. .. .. .. "
#endif
	       "%-30s "
	       "%4s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s\n",
		"CS", "IP", "instruction",
		"AX", "BX", "CX", "DX",
		"DI", "SI", "SP", "BP",
		"SS", "DS", "ES");
	first = 0;
    }

    fprintf(debugf, bigfmt,
	    GET16(sc->sc_cs), GET16(sc->sc_eip),
#if BIG_DEBUG
	    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5],
#endif
	    buf,
	    GET16(sc->sc_eax), GET16(sc->sc_ebx), GET16(sc->sc_ecx), GET16(sc->sc_edx),
	    GET16(sc->sc_edi), GET16(sc->sc_esi), GET16(sc->sc_esp), GET16(sc->sc_ebp),
	    GET16(sc->sc_ss), GET16(sc->sc_ds), GET16(sc->sc_es));
#if 0
    fprintf(debugf, "%04x %04x %04x %04x ",
	    ((u_short *)VECPTR(0x0D760FCA-14))[0],
	    ((u_short *)VECPTR(0x0D760FCA-14))[1],
	    ((u_short *)VECPTR(0x0D760F7A+8))[0],
	    ((u_short *)VECPTR(0x0D760F7A+8))[1]);
#endif
    showstate(sc->sc_eflags, PSL_C, 'C');
    showstate(sc->sc_eflags, PSL_PF, 'P');
    showstate(sc->sc_eflags, PSL_AF, 'c');
    showstate(sc->sc_eflags, PSL_Z, 'Z');
    showstate(sc->sc_eflags, PSL_N, 'N');
    showstate(sc->sc_eflags, PSL_T, 'T');
    showstate(sc->sc_eflags, PSL_I, 'I');
    showstate(sc->sc_eflags, PSL_D, 'D');
    showstate(sc->sc_eflags, PSL_V, 'V');
    showstate(sc->sc_eflags, PSL_NT, 'n');
    showstate(sc->sc_eflags, PSL_RF, 'r');
    showstate(sc->sc_eflags, PSL_VM, 'v');
    showstate(sc->sc_eflags, PSL_AC, 'a');
    putc('\n', debugf);
}
