/* Copyright (c)1994-2000 Begemot Computer Associates. All rights reserved.
 * See the file COPYRIGHT for details of redistribution and use. */

/*
 * processor trap, interrupt and DMA handling
 */

# include "proc.h"

RCSID("$Id: trap.c 472 2001-03-15 14:36:02Z hbb $")

# define D(C)  /* printf C */

# define RED	1
# define YELLOW	2

static void Push(u_short);

/*
 * old PC and PSW are not local because if we enter SetupTrap a
 * second time because of an push abort we need the original
 * PC and PSW for the red stack trap
 */
static int	y_or_r = 0;	/* true during yellow or red stack trap */
static int	fetching = 0;	/* true when fetching vector */
static int 	pushing = 0;	/* true when pushing psw and pc */
static int	opsw;		/* old psw */
static int	opc;		/* old pc */

void
YellowTrap()
{
	D(("YELLOW TRAP during push\n"));
	y_or_r = YELLOW;
	proc.cpuerr |= 010;
	Trap(4);
}


void
Trap10(void)
{
	if(proc.stop10)
		proc.halt = 1;
	Trap(010);
}

void
Trap4(u_short err)
{
	proc.cpuerr |= err;
	if(proc.stop4)
		proc.halt = 1;
	Trap(4);
}

void
Trap(u_short vec)
{
	SetupTrap(vec);
	longjmp(trap, 1);
}

void
SetupTrap(u_short vec)
{
	u_short psw, pc, prevmode;

again:
	vec &= 0774;

	if(pushing) {
		/*
		 * a trap occures during a trap push
		 * do a red stack trap
		 */
		D(("RED TRAP\n"));
		vec = 4;
		proc.reg[6] = 4;
		proc.cpuerr |= 020;
		pushing = 0;
		y_or_r = RED;
	}
	if(fetching) {
		/*
		 * a vector fetch abort, stop the emulator
		 */
		fetching = 0;
		if(proc.maint & CPU_PWOK)	/* power ok */
			printf("vector fetch abort (%ho)\n", vec);
		monitor();
	}

	D(("Trap to %ho  ", vec));

	/*
	 * fetch new ps and pc from kernel D-space
	 * set a flag to stop the emulator if this traps
	 * this means kernal D-space page 0 is bad, so what can we do?
	 */
	fetching = 1;
	psw = mmfetch(vec+2, 1, 0);
	pc  = mmfetch(vec,   1, 0);
	fetching = 0;

	/*
	 * get old psw and implicite switch to new stack
	 * don't forget about T-bit
	 */
	if(y_or_r != RED) {
		opsw = iofetch(017777776);
		opc = proc.reg[7];
	}

	prevmode = proc.curmode;
	iostore(017777776, psw);
	proc.t = (psw >> 4) & 1;
	proc.prevmode = prevmode;
	proc.reg[7] = pc;

	/*
	 * now push pc and psw, switch to new pc
	 * if the pushes abort we must do a red stack trap in kernal mode
	 * if the stack pointer drops below 400 in kernal mode do a yellow trap
	 */
	D(("PC=%ho PSW=%ho -> PC=%ho PSW=%ho\n", opc, opsw, pc, psw));

	pushing = (proc.curmode == 0);
	Push(opsw);
	Push(opc);
	pushing = 0;

	if(!y_or_r && proc.curmode == 0 && proc.reg[6] < 0400) {
		D(("YELLOW TRAP during trap push\n"));
		vec = 4;
		proc.cpuerr |= 010;
		y_or_r = YELLOW;
		goto again;
	}
	y_or_r = 0;
}

/*
 * switch SP and/or general register set
 */
void
switchmode(int mode, int regs)
{
	if(regs && !proc.regs) {
		memcpy(proc.ureg, proc.reg, sizeof(proc.ureg));
		memcpy(proc.reg, proc.kreg, sizeof(proc.kreg));
		D(("switch to alternate registers\n"));
	} else if(!regs && proc.regs) {
		memcpy(proc.kreg, proc.reg, sizeof(proc.kreg));
		memcpy(proc.reg, proc.ureg, sizeof(proc.ureg));
		D(("switch to normal registers\n"));
	}
	proc.sp[proc.curmode] = proc.reg[6];
	proc.reg[6] = proc.sp[mode];
	D(("switchmode %d -> %d at %ho\n", proc.curmode, mode, proc.reg[7]));
}


/*
 * Process DMA and/or interrupt requests
 */
void
request(void)
{
	iodev_t *d, *f;
	sigset_t oldmask, mask;

	sigemptyset(&mask);
	sigaddset(&mask, SIGIO);
	sigaddset(&mask, SIGALRM);
	sigprocmask(SIG_BLOCK, &mask, &oldmask);

	for(;;) {
		/*
		 * find maximum request, nearest to processor
		 */
		proc.reqpri = 0;
		f = NULL;
		for(d = proc.devices; d != NULL; d = d->next)
			if(d->reqpri > proc.reqpri) {
				proc.reqpri = d->reqpri;
				f = d;
			}
		if(proc.reqpri <= proc.pri)
			break;

		/*
		 * call dma routine or set up vectored interrupt
		 * routine should clear request
		 */
		if(proc.reqpri == DMAPRI)
			(*f->ioops->dma)(f);
		else
			SetupTrap((*f->ioops->vector)(f));
	}

	sigprocmask(SIG_SETMASK, &oldmask, NULL);
}

static void
Push(u_short v)
{
	if((proc.reg[6] -= 2) & 1)
		Trap4(0100);

	mstore(proc.reg[6], 1, v);
}
