/*
 * 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 trap.c,v 2.3 1996/04/08 19:33:08 bostic Exp
 */

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

void
fake_int(struct sigcontext *sc, int intnum)
{
    	if (GET16(sc->sc_cs) == 0xF000 || (ivec[intnum] >> 16) == 0xF000) {
		if (GET16(sc->sc_cs) != 0xF000)
			intnum = ((u_char *)VECPTR(ivec[intnum]))[1];
		debug (D_ITRAPS|intnum, "int%02x:%02x %04x:%04x/%08x",
		       intnum, GET8H(sc->sc_eax), GET16(sc->sc_cs),
		       GET16(sc->sc_eip), ivec[intnum]);
		switch (intnum) {
		case 0x2f: int2f(sc); break;
		case 0xff: intff(sc); break;
		case 0xfe: if (raw_kbd == 2) quit(0);
		default:
			if (vflag) dump_regs(sc);
			fatal("no interrupt set up for 0x%02x\n", intnum);
		}
		debug (D_ITRAPS|intnum, "\n");
		return;
	}

user_int:
	debug (D_TRAPS|intnum, "INT%02x:%02x [%04x:%04x] %04x %04x %04x %04x\n",
	      intnum, GET8H(sc->sc_eax),
	      ivec[intnum] >> 16, ivec[intnum] & 0xffff,
	      GET16(sc->sc_eax), GET16(sc->sc_ebx), GET16(sc->sc_ecx), GET16(sc->sc_edx));

	if (ivec[intnum] == 0) {
		if (vflag) dump_regs(sc);
		fatal("no interrupt set up for 0x%02x\n", intnum);
	}

	/*
	 * This is really ugly, but when DOS boots, it seems to loop
	 * for a while on INT 16:11 INT 21:3E INT 2A:82
	 * INT 21:3E is a close(), which seems like something one would
	 * not sit on for ever, so we will allow it to reset our POLL count.
	 */
    	if (intnum == 0x21 && GET8H(sc->sc_eax) == 0x3E)
	    	reset_poll();

	PUSH(GET16(sc->sc_eflags), sc);
	PUSH(GET16(sc->sc_cs), sc);
	PUSH(GET16(sc->sc_eip), sc);
	PUTVEC(sc->sc_cs, sc->sc_eip, ivec[intnum]);
}

void
sigurg(struct sigframe *sf)
{
#define	sc	(&sf->sf_sc)
	int intnum;
	u_char *addr;
    	int rep;
	int port;
	callback_t func;

#if 0
	printf("ivec08 = %08x\n", ivec[0x08]);
#endif

    	if (tmode)
		resettrace(sc);

	switch (VM86_TYPE(sf->sf_code)) {
	case VM86_INTx:
		intnum = VM86_ARG(sf->sf_code);
		switch (intnum) {
		case 0x2f:
			switch (GET8H(sc->sc_eax)) {
			case 0x11:
				debug (D_TRAPS|0x2f, "INT 2F:%04x\n", GET16(sc->sc_eax));
				if (int2f_11(sc)) {
					/* Skip over int 2f:11 */
					goto out;
				}
				break;
			case 0x43:
				debug (D_TRAPS|0x2f, "INT 2F:%04x\n", GET16(sc->sc_eax));
				if (int2f_43(sc)) {
					/* Skip over int 2f:43 */
					goto out;
				}
				break;
			}
			break;
		}
		fake_int(sc, intnum);
		break;
	case VM86_UNKNOWN:
		/*XXXXX failed vector also gets here without IP adjust*/

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

		debug (D_TRAPS2, "%04x:%04x [%02x]", GET16(sc->sc_cs), GET16(sc->sc_eip), addr[0]);
		switch (addr[0]) {
    		case TRACETRAP:
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 1);
			fake_int(sc, 3);
			break;
		case INd:
			port = addr[1];
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 2);
			inb(sc, port);
			break;
		case OUTd:
			port = addr[1];
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 2);
			outb(sc, port);
			break;
		case INdX:
			port = addr[1];
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 2);
			inx(sc, port);
			break;
		case OUTdX:
			port = addr[1];
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 2);
			outx(sc, port);
			break;
		case IN:
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 1);
		    	inb(sc, GET16(sc->sc_edx));
			break;
		case INX:
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 1);
			inx(sc, GET16(sc->sc_edx));
		    	break;
		case OUT:
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 1);
		    	outb(sc, GET16(sc->sc_edx));
			break;
		case OUTX:
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 1);
		    	outx(sc, GET16(sc->sc_edx));
			break;
		case OUTSB:
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 1);
    		    	while (rep-- > 0)
			    	outsb(sc, GET16(sc->sc_edx));
			break;
		case OUTSW:
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 1);
    		    	while (rep-- > 0)
			    	outsx(sc, GET16(sc->sc_edx));
			break;
		case INSB:
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 1);
			while (rep-- > 0)
				insb(sc, GET16(sc->sc_edx));
			break;
		case INSW:
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 1);
			while (rep-- > 0)
				insx(sc, GET16(sc->sc_edx));
			break;
		case LOCK:
			debug(D_TRAPS2, "lock\n");
			SET16(sc->sc_eip, GET16(sc->sc_eip) + 1);
			break;
		case HLT:
			func = find_callback(GETVEC(sc->sc_cs, sc->sc_eip));
			if (func) {
				SET16(sc->sc_eip, GET16(sc->sc_eip) + 1);
				func(sc);
				break;
			}
		default:
			dump_regs(sc);
			fatal("unsupported instruction\n");
		}
		break;
	default:
		dump_regs(sc);
		printf("code = %04x\n", sf->sf_code);
		fatal("unrecognized vm86 trap\n");
	}

out:
    	if (tmode)
		tracetrap(sc);
#undef	sc
}

void
sigtrace(struct sigframe *sf)
{
    int x;

    if (sf->sf_sc.sc_eflags & PSL_VM)
	fprintf(debugf, "Currently in DOS\n");
    dump_regs(&sf->sf_sc);
    for (x = 0; x < 16; ++x)
	fprintf(debugf, " %02x", *(unsigned char *)x);
    putc('\n', debugf);
}

void
sigtrap(struct sigframe *sf)
{   
#define	sc	(&sf->sf_sc)
	int	intnum;

	if ((sc->sc_eflags & PSL_VM) == 0) {
		dump_regs(sc);
		fatal("%04x:%08x Sigtrap in protected mode\n",
		    GET16(sc->sc_cs), sc->sc_eip);
	}

    	if (tmode)
		if (resettrace(sc))
			goto doh;

	if (sc->sc_trapno == T_BPTFLT)
		intnum = 3;  
	else
		intnum = 1;

	PUSH(GET16(sc->sc_eflags), sc);
	PUSH(GET16(sc->sc_cs), sc);
	PUSH(GET16(sc->sc_eip), sc);
	sc->sc_eflags &= ~PSL_T;
	PUTVEC(sc->sc_cs, sc->sc_eip, ivec[intnum]);

doh:
    	if (tmode)
		tracetrap(sc);
#undef	sc
}

void
breakpoint(struct sigframe *sf)
{
#define	sc	(&sf->sf_sc)
        if (sc->sc_eflags & PSL_VM)
		printf("doscmd ");
	printf("breakpoint: %04x\n", *(u_short *)0x8e64);

        __asm__ volatile("mov 0, %eax");
        __asm__ volatile(".byte 0x0f");		/* MOV DR6,EAX */
        __asm__ volatile(".byte 0x21");
        __asm__ volatile(".byte 0x1b");
#undef	sc
}


void
sigalrm(struct sigframe *sf)
{
#define	sc	(&sf->sf_sc)
	if (tmode)
		resettrace(sc);

	video_update(sc);
	hardint(0x08);

	if (tmode)
		tracetrap(sc);
#undef	sc
}

void
sigill(struct sigframe *sf)
{
#define	sc	(&sf->sf_sc)
fprintf(stderr, "Signal %d from DOS program\n", sf->sf_signum);
	dump_regs(sc);
	fatal("%04x:%04x Illegal instruction\n",
	    GET16(sc->sc_cs), GET16(sc->sc_eip));
#undef	sc
}

void
sigfpe(struct sigframe *sf)
{
#define	sc	(&sf->sf_sc)
#if 1
        if (sc->sc_eflags & PSL_VM) {
	    if ((ivec[0] >> 16) != 0xF000)
		fake_int(sc, 0);
	    else
		printf("Divide by 0!\n");
	    return;
    	}
#endif
	dump_regs(sc);
	fatal("%04x:%04x Floating point fault\n",
	    GET16(sc->sc_cs), GET16(sc->sc_eip));
#undef	sc
}
