NetBSD-5.0.2/sys/arch/mips/mips/mips_emul.c

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: mips_emul.c,v 1.14 2006/08/26 20:15:28 matt Exp $ */

/*
 * Copyright (c) 1999 Shuichiro URATA.  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. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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.
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mips_emul.c,v 1.14 2006/08/26 20:15:28 matt Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/user.h>

#include <mips/locore.h>
#include <mips/mips_opcode.h>

#include <machine/cpu.h>
#include <mips/reg.h>
#include <mips/regnum.h>			/* symbolic register indices */
#include <mips/vmparam.h>			/* for VM_MAX_ADDRESS */
#include <mips/trap.h>

void MachEmulateFP(u_int32_t, struct frame *, u_int32_t);

static inline void	send_sigsegv(vaddr_t, u_int32_t, struct frame *,
			    u_int32_t);
static inline void	update_pc(struct frame *, u_int32_t);

vaddr_t MachEmulateBranch(struct frame *, vaddr_t, unsigned, int);
void	MachEmulateInst(u_int32_t, u_int32_t, vaddr_t, struct frame *);

void	MachEmulateLWC0(u_int32_t inst, struct frame *, u_int32_t);
void	MachEmulateSWC0(u_int32_t inst, struct frame *, u_int32_t);
void	MachEmulateSpecial(u_int32_t inst, struct frame *, u_int32_t);
void	MachEmulateLWC1(u_int32_t inst, struct frame *, u_int32_t);
void	MachEmulateLDC1(u_int32_t inst, struct frame *, u_int32_t);
void	MachEmulateSWC1(u_int32_t inst, struct frame *, u_int32_t);
void	MachEmulateSDC1(u_int32_t inst, struct frame *, u_int32_t);

void	bcemul_lb(u_int32_t inst, struct frame *, u_int32_t);
void	bcemul_lbu(u_int32_t inst, struct frame *, u_int32_t);
void	bcemul_lh(u_int32_t inst, struct frame *, u_int32_t);
void	bcemul_lhu(u_int32_t inst, struct frame *, u_int32_t);
void	bcemul_lw(u_int32_t inst, struct frame *, u_int32_t);
void	bcemul_lwl(u_int32_t inst, struct frame *, u_int32_t);
void	bcemul_lwr(u_int32_t inst, struct frame *, u_int32_t);
void	bcemul_sb(u_int32_t inst, struct frame *, u_int32_t);
void	bcemul_sh(u_int32_t inst, struct frame *, u_int32_t);
void	bcemul_sw(u_int32_t inst, struct frame *, u_int32_t);
void	bcemul_swl(u_int32_t inst, struct frame *, u_int32_t);
void	bcemul_swr(u_int32_t inst, struct frame *f, u_int32_t);

/*
 * MIPS2 LL instruction emulation state
 */
struct {
	struct lwp *lwp;
	vaddr_t addr;
	u_int32_t value;
} llstate;

/*
 * Analyse 'next' PC address taking account of branch/jump instructions
 */
vaddr_t
MachEmulateBranch(f, instpc, fpuCSR, allowNonBranch)
	struct frame *f;
	vaddr_t instpc;
	unsigned fpuCSR;
	int allowNonBranch;
{
#define	BRANCHTARGET(p) (4 + (p) + ((short)((InstFmt *)(p))->IType.imm << 2))
	InstFmt inst;
	vaddr_t nextpc;

	if (instpc < MIPS_KSEG0_START)
		inst.word = fuiword((void *)instpc);
	else
		inst.word = *(unsigned *)instpc;

	switch ((int)inst.JType.op) {
	case OP_SPECIAL:
		if (inst.RType.func == OP_JR || inst.RType.func == OP_JALR)
			nextpc = f->f_regs[inst.RType.rs];
		else if (allowNonBranch)
			nextpc = instpc + 4;
		else
			panic("MachEmulateBranch: Non-branch instruction at "
			    "pc 0x%lx", (long)instpc);
		break;

	case OP_BCOND:
		switch ((int)inst.IType.rt) {
		case OP_BLTZ:
		case OP_BLTZAL:
		case OP_BLTZL:		/* squashed */
		case OP_BLTZALL:	/* squashed */
			if ((int)(f->f_regs[inst.RType.rs]) < 0)
				nextpc = BRANCHTARGET(instpc);
			else
				nextpc = instpc + 8;
			break;

		case OP_BGEZ:
		case OP_BGEZAL:
		case OP_BGEZL:		/* squashed */
		case OP_BGEZALL:	/* squashed */
			if ((int)(f->f_regs[inst.RType.rs]) >= 0)
				nextpc = BRANCHTARGET(instpc);
			else
				nextpc = instpc + 8;
			break;

		default:
			panic("MachEmulateBranch: Bad branch cond");
		}
		break;

	case OP_J:
	case OP_JAL:
		nextpc = (inst.JType.target << 2) |
			((unsigned)instpc & 0xF0000000);
		break;

	case OP_BEQ:
	case OP_BEQL:	/* squashed */
		if (f->f_regs[inst.RType.rs] == f->f_regs[inst.RType.rt])
			nextpc = BRANCHTARGET(instpc);
		else
			nextpc = instpc + 8;
		break;

	case OP_BNE:
	case OP_BNEL:	/* squashed */
		if (f->f_regs[inst.RType.rs] != f->f_regs[inst.RType.rt])
			nextpc = BRANCHTARGET(instpc);
		else
			nextpc = instpc + 8;
		break;

	case OP_BLEZ:
	case OP_BLEZL:	/* squashed */
		if ((int)(f->f_regs[inst.RType.rs]) <= 0)
			nextpc = BRANCHTARGET(instpc);
		else
			nextpc = instpc + 8;
		break;

	case OP_BGTZ:
	case OP_BGTZL:	/* squashed */
		if ((int)(f->f_regs[inst.RType.rs]) > 0)
			nextpc = BRANCHTARGET(instpc);
		else
			nextpc = instpc + 8;
		break;

	case OP_COP1:
		if (inst.RType.rs == OP_BCx || inst.RType.rs == OP_BCy) {
			int condition = (fpuCSR & MIPS_FPU_COND_BIT) != 0;
			if ((inst.RType.rt & COPz_BC_TF_MASK) != COPz_BC_TRUE)
				condition = !condition;
			if (condition)
				nextpc = BRANCHTARGET(instpc);
			else
				nextpc = instpc + 8;
		}
		else if (allowNonBranch)
			nextpc = instpc + 4;
		else
			panic("MachEmulateBranch: Bad COP1 branch instruction");
		break;

	default:
		if (!allowNonBranch)
			panic("MachEmulateBranch: Non-branch instruction at "
			    "pc 0x%lx", (long)instpc);
		nextpc = instpc + 4;
	}
	return nextpc;
#undef	BRANCHTARGET
}

/*
 * Emulate instructions (including floating-point instructions)
 */
void
MachEmulateInst(status, cause, opc, frame)
	u_int32_t status;
	u_int32_t cause;
	vaddr_t opc;
	struct frame *frame;
{
	u_int32_t inst;
	ksiginfo_t ksi;

	/*
	 *  Fetch the instruction.
	 */
	if (cause & MIPS_CR_BR_DELAY)
		inst = fuword((u_int32_t *)opc+1);
	else
		inst = fuword((u_int32_t *)opc);

	switch (((InstFmt)inst).FRType.op) {
	case OP_LWC0:
		MachEmulateLWC0(inst, frame, cause);
		break;
	case OP_SWC0:
		MachEmulateSWC0(inst, frame, cause);
		break;
	case OP_SPECIAL:
		MachEmulateSpecial(inst, frame, cause);
		break;
	case OP_COP1:
		MachEmulateFP(inst, frame, cause);
		break;
#if defined(SOFTFLOAT)
	case OP_LWC1:
		MachEmulateLWC1(inst, frame, cause);
		break;
	case OP_LDC1:
		MachEmulateLDC1(inst, frame, cause);
		break;
	case OP_SWC1:
		MachEmulateSWC1(inst, frame, cause);
		break;
	case OP_SDC1:
		MachEmulateSDC1(inst, frame, cause);
		break;
#endif
	default:
		frame->f_regs[_R_CAUSE] = cause;
		frame->f_regs[_R_BADVADDR] = opc;
		KSI_INIT_TRAP(&ksi);
		ksi.ksi_signo = SIGSEGV;
		ksi.ksi_trap = cause; /* XXX */
		ksi.ksi_code = SEGV_MAPERR;
		ksi.ksi_addr = (void *)opc;
		(*curproc->p_emul->e_trapsignal)(curlwp, &ksi);
		break;
	}
}

static inline void
send_sigsegv(vaddr_t vaddr, u_int32_t exccode, struct frame *frame,
    u_int32_t cause)
{
	ksiginfo_t ksi;
	cause = (cause & 0xFFFFFF00) | (exccode << MIPS_CR_EXC_CODE_SHIFT);
	frame->f_regs[_R_CAUSE] = cause;
	frame->f_regs[_R_BADVADDR] = vaddr;
	KSI_INIT_TRAP(&ksi);
	ksi.ksi_signo = SIGSEGV;
	ksi.ksi_trap = cause;
	ksi.ksi_code = SEGV_MAPERR;
	ksi.ksi_addr = (void *)vaddr;
	(*curproc->p_emul->e_trapsignal)(curlwp, &ksi);
}

static inline void
update_pc(struct frame *frame, u_int32_t cause)
{

	if (cause & MIPS_CR_BR_DELAY)
		frame->f_regs[_R_PC] = 
		    MachEmulateBranch(frame, frame->f_regs[_R_PC],
			PCB_FSR(curpcb), 0);
	else
		frame->f_regs[_R_PC] += 4;
}

/*
 * MIPS2 LL instruction
 */
void
MachEmulateLWC0(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	vaddr_t		vaddr;
	int16_t		offset;
	void		*t;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment and alignment check */
	if (vaddr > VM_MAX_ADDRESS || vaddr & 0x3) {
		ksiginfo_t ksi;
		frame->f_regs[_R_CAUSE] = cause;
		frame->f_regs[_R_BADVADDR] = vaddr;
		KSI_INIT_TRAP(&ksi);
		ksi.ksi_signo = SIGBUS;
		ksi.ksi_trap = cause;
		ksi.ksi_code = (vaddr & 3) ? BUS_ADRALN : BUS_ADRERR;
		ksi.ksi_addr = (void *)vaddr;
		(*curproc->p_emul->e_trapsignal)(curlwp, &ksi);
		return;
	}

	t = &(frame->f_regs[(inst>>16)&0x1F]);

	if (copyin((void *)vaddr, t, 4) != 0) {
		send_sigsegv(vaddr, T_TLB_LD_MISS, frame, cause);
		return;
	}

	llstate.lwp = curlwp;
	llstate.addr = vaddr;
	llstate.value = *((u_int32_t *)t);

	update_pc(frame, cause);
}

/*
 * MIPS2 SC instruction
 */
void
MachEmulateSWC0(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	vaddr_t		vaddr;
	uint32_t	value;
	int16_t		offset;
	mips_reg_t	*t;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment and alignment check */
	if (vaddr > VM_MAX_ADDRESS || vaddr & 0x3) {
		ksiginfo_t ksi;
		frame->f_regs[_R_CAUSE] = cause;
		frame->f_regs[_R_BADVADDR] = vaddr;
		KSI_INIT_TRAP(&ksi);
		ksi.ksi_signo = SIGBUS;
		ksi.ksi_trap = cause;
		ksi.ksi_code = (vaddr & 3) ? BUS_ADRALN : BUS_ADRERR;
		ksi.ksi_addr = (void *)vaddr;
		(*curproc->p_emul->e_trapsignal)(curlwp, &ksi);
		return;
	}

	t = (mips_reg_t *)&(frame->f_regs[(inst>>16)&0x1F]);

	/*
	 * Check that the process and address match the last
	 * LL instruction.
	 */
	if (curlwp == llstate.lwp && vaddr == llstate.addr) {
		llstate.lwp = NULL;
		/*
		 * Check that the data at the address hasn't changed
		 * since the LL instruction.
		 */
		if (copyin((void *)vaddr, &value, 4) != 0) {
			send_sigsegv(vaddr, T_TLB_LD_MISS, frame, cause);
			return;
		}
		if (value == llstate.value) {
			/* SC successful */
			if (copyout(t, (void *)vaddr, 4) != 0) {
				send_sigsegv(vaddr, T_TLB_ST_MISS,
				    frame, cause);
				return;
			}
			*t = 1;
			update_pc(frame, cause);
			return;
		}
	}

	/* SC failed */
	*t = 0;
	update_pc(frame, cause);
}

void
MachEmulateSpecial(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	ksiginfo_t ksi;
	switch (((InstFmt)inst).RType.func) {
	case OP_SYNC:
		/* nothing */
		break;
	default:
		frame->f_regs[_R_CAUSE] = cause;
		frame->f_regs[_R_BADVADDR] = frame->f_regs[_R_PC];
		KSI_INIT_TRAP(&ksi);
		ksi.ksi_signo = SIGSEGV;
		ksi.ksi_trap = cause;
		ksi.ksi_code = SEGV_MAPERR;
		ksi.ksi_addr = (void *)frame->f_regs[_R_PC];
		(*curproc->p_emul->e_trapsignal)(curlwp, &ksi);
		break;
	}

	update_pc(frame, cause);
}

#if defined(SOFTFLOAT)

#define LWSWC1_MAXLOOP	12

void
MachEmulateLWC1(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr;
	int16_t		offset;
	void		*t;
	mips_reg_t	pc;
	int		i;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment and alignment check */
	if (vaddr > VM_MAX_ADDRESS || vaddr & 0x3) {
		send_sigsegv(vaddr, T_ADDR_ERR_LD, frame, cause);
		return;
	}

	t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1F]);

	if (copyin((void *)vaddr, t, 4) != 0) {
		send_sigsegv(vaddr, T_TLB_LD_MISS, frame, cause);
		return;
	}

	pc = frame->f_regs[_R_PC];
	update_pc(frame, cause);

	if (cause & MIPS_CR_BR_DELAY)
		return;

	for (i = 1; i < LWSWC1_MAXLOOP; i++) {
		if (mips_btop(frame->f_regs[_R_PC]) != mips_btop(pc))
			return;

		vaddr = frame->f_regs[_R_PC];	/* XXX truncates to 32 bits */
		inst = fuiword((u_int32_t *)vaddr);
		if (((InstFmt)inst).FRType.op != OP_LWC1)
			return;

		offset = inst & 0xFFFF;
		vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

		/* segment and alignment check */
		if (vaddr > VM_MAX_ADDRESS || vaddr & 0x3) {
			send_sigsegv(vaddr, T_ADDR_ERR_LD, frame, cause);
			return;
		}

		t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1F]);

		if (copyin((void *)vaddr, t, 4) != 0) {
			send_sigsegv(vaddr, T_TLB_LD_MISS, frame, cause);
			return;
		}

		pc = frame->f_regs[_R_PC];
		update_pc(frame, cause);
	}
}

void
MachEmulateLDC1(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr;
	int16_t		offset;
	void		*t;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment and alignment check */
	if (vaddr & 0x80000007) {
		send_sigsegv(vaddr, T_ADDR_ERR_LD, frame, cause);
		return;
	}

	t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1E]);

	if (copyin((void *)vaddr, t, 8) != 0) {
		send_sigsegv(vaddr, T_TLB_LD_MISS, frame, cause);
		return;
	}

	update_pc(frame, cause);
}

void
MachEmulateSWC1(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr;
	int16_t		offset;
	void		*t;
	mips_reg_t	pc;
	int		i;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment and alignment check */
	if (vaddr > VM_MAX_ADDRESS || vaddr & 0x3) {
		send_sigsegv(vaddr, T_ADDR_ERR_ST, frame, cause);
		return;
	}

	t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1F]);

	if (copyout(t, (void *)vaddr, 4) != 0) {
		send_sigsegv(vaddr, T_TLB_ST_MISS, frame, cause);
		return;
	}

	pc = frame->f_regs[_R_PC];
	update_pc(frame, cause);

	if (cause & MIPS_CR_BR_DELAY)
		return;

	for (i = 1; i < LWSWC1_MAXLOOP; i++) {
		if (mips_btop(frame->f_regs[_R_PC]) != mips_btop(pc))
			return;

		vaddr = frame->f_regs[_R_PC];	/* XXX truncates to 32 bits */
		inst = fuiword((u_int32_t *)vaddr);
		if (((InstFmt)inst).FRType.op != OP_SWC1)
			return;

		offset = inst & 0xFFFF;
		vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

		/* segment and alignment check */
		if (vaddr > VM_MAX_ADDRESS || vaddr & 0x3) {
			send_sigsegv(vaddr, T_ADDR_ERR_ST, frame, cause);
			return;
		}

		t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1F]);

		if (copyout(t, (void *)vaddr, 4) != 0) {
			send_sigsegv(vaddr, T_TLB_ST_MISS, frame, cause);
			return;
		}

		pc = frame->f_regs[_R_PC];
		update_pc(frame, cause);
	}
}

void
MachEmulateSDC1(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr;
	int16_t		offset;
	void		*t;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment and alignment check */
	if (vaddr & 0x80000007) {
		send_sigsegv(vaddr, T_ADDR_ERR_ST, frame, cause);
		return;
	}

	t = &(curpcb->pcb_fpregs.r_regs[(inst>>16)&0x1E]);

	if (copyout(t, (void *)vaddr, 8) != 0) {
		send_sigsegv(vaddr, T_TLB_ST_MISS, frame, cause);
		return;
	}

	update_pc(frame, cause);
}

void
bcemul_lb(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr;
	int16_t		offset;
	int8_t		x;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment check */
	if (vaddr & 0x80000000) {
		send_sigsegv(vaddr, T_ADDR_ERR_LD, frame, cause);
		return;
	}

	if (copyin((void *)vaddr, &x, 1) != 0) {
		send_sigsegv(vaddr, T_TLB_LD_MISS, frame, cause);
		return;
	}

	frame->f_regs[(inst>>16)&0x1F] = (int32_t)x;

	update_pc(frame, cause);
}

void
bcemul_lbu(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr;
	int16_t		offset;
	u_int8_t	x;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment check */
	if (vaddr & 0x80000000) {
		send_sigsegv(vaddr, T_ADDR_ERR_LD, frame, cause);
		return;
	}

	if (copyin((void *)vaddr, &x, 1) != 0) {
		send_sigsegv(vaddr, T_TLB_LD_MISS, frame, cause);
		return;
	}

	frame->f_regs[(inst>>16)&0x1F] = (u_int32_t)x;

	update_pc(frame, cause);
}

void
bcemul_lh(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr;
	int16_t		offset;
	int16_t		x;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment and alignment check */
	if (vaddr & 0x80000001) {
		send_sigsegv(vaddr, T_ADDR_ERR_LD, frame, cause);
		return;
	}

	if (copyin((void *)vaddr, &x, 2) != 0) {
		send_sigsegv(vaddr, T_TLB_LD_MISS, frame, cause);
		return;
	}

	frame->f_regs[(inst>>16)&0x1F] = (int32_t)x;

	update_pc(frame, cause);
}

void
bcemul_lhu(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr;
	int16_t		offset;
	u_int16_t	x;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment and alignment check */
	if (vaddr & 0x80000001) {
		send_sigsegv(vaddr, T_ADDR_ERR_LD, frame, cause);
		return;
	}

	if (copyin((void *)vaddr, &x, 2) != 0) {
		send_sigsegv(vaddr, T_TLB_LD_MISS, frame, cause);
		return;
	}

	frame->f_regs[(inst>>16)&0x1F] = (u_int32_t)x;

	update_pc(frame, cause);
}

void
bcemul_lw(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr;
	int16_t		offset;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment and alignment check */
	if (vaddr > VM_MAX_ADDRESS || vaddr & 0x3) {
		send_sigsegv(vaddr, T_ADDR_ERR_LD, frame, cause);
		return;
	}

	if (copyin((void *)vaddr, &(frame->f_regs[(inst>>16)&0x1F]), 4) != 0) {
		send_sigsegv(vaddr, T_TLB_LD_MISS, frame, cause);
		return;
	}

	update_pc(frame, cause);
}

void
bcemul_lwl(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr, a, x, shift;
	int16_t		offset;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment check */
	if (vaddr & 0x80000000) {
		send_sigsegv(vaddr, T_ADDR_ERR_LD, frame, cause);
		return;
	}

	if (copyin((void *)(vaddr & ~0x3), &a, 4) != 0) {
		send_sigsegv(vaddr, T_TLB_LD_MISS, frame, cause);
		return;
	}

	x = frame->f_regs[(inst>>16)&0x1F];

	shift = (3 - (vaddr & 0x00000003)) * 8;
	a <<= shift;
	x &= ~(0xFFFFFFFFUL << shift);
	x |= a;

	frame->f_regs[(inst>>16)&0x1F] = x;

	update_pc(frame, cause);
}

void
bcemul_lwr(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr, a, x, shift;
	int16_t		offset;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment check */
	if (vaddr & 0x80000000) {
		send_sigsegv(vaddr, T_ADDR_ERR_LD, frame, cause);
		return;
	}

	if (copyin((void *)(vaddr & ~0x3), &a, 4) != 0) {
		send_sigsegv(vaddr, T_TLB_LD_MISS, frame, cause);
		return;
	}

	x = frame->f_regs[(inst>>16)&0x1F];

	shift = (vaddr & 0x00000003) * 8;
	a >>= shift;
	x &= ~(0xFFFFFFFFUL >> shift);
	x |= a;

	frame->f_regs[(inst>>16)&0x1F] = x;

	update_pc(frame, cause);
}

void
bcemul_sb(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr;
	int16_t		offset;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment check */
	if (vaddr & 0x80000000) {
		send_sigsegv(vaddr, T_ADDR_ERR_ST, frame, cause);
		return;
	}

	if (subyte((void *)vaddr, frame->f_regs[(inst>>16)&0x1F]) < 0) {
		send_sigsegv(vaddr, T_TLB_ST_MISS, frame, cause);
		return;
	}

	update_pc(frame, cause);
}

void
bcemul_sh(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr;
	int16_t		offset;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment and alignment check */
	if (vaddr & 0x80000001) {
		send_sigsegv(vaddr, T_ADDR_ERR_ST, frame, cause);
		return;
	}

	if (susword((void *)vaddr, frame->f_regs[(inst>>16)&0x1F]) < 0) {
		send_sigsegv(vaddr, T_TLB_ST_MISS, frame, cause);
		return;
	}

	update_pc(frame, cause);
}

void
bcemul_sw(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr;
	int16_t		offset;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment and alignment check */
	if (vaddr > VM_MAX_ADDRESS || vaddr & 0x3) {
		send_sigsegv(vaddr, T_ADDR_ERR_ST, frame, cause);
		return;
	}

	if (suword((void *)vaddr, frame->f_regs[(inst>>16)&0x1F]) < 0) {
		send_sigsegv(vaddr, T_TLB_ST_MISS, frame, cause);
		return;
	}

	update_pc(frame, cause);
}

void
bcemul_swl(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr, a, x, shift;
	int16_t		offset;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment check */
	if (vaddr & 0x80000000) {
		send_sigsegv(vaddr, T_ADDR_ERR_ST, frame, cause);
		return;
	}

	if (copyin((void *)(vaddr & ~0x3), &a, 4) != 0) {
		send_sigsegv(vaddr, T_TLB_ST_MISS, frame, cause);
		return;
	}

	x = frame->f_regs[(inst>>16)&0x1F];

	shift = (3 - (vaddr & 0x00000003)) * 8;
	x >>= shift;
	a &= ~(0xFFFFFFFFUL >> shift);
	a |= x;

	if (suword((void *)vaddr, a) < 0) {
		send_sigsegv(vaddr, T_TLB_ST_MISS, frame, cause);
		return;
	}

	update_pc(frame, cause);
}

void
bcemul_swr(u_int32_t inst, struct frame *frame, u_int32_t cause)
{
	u_int32_t	vaddr, a, x, shift;
	int16_t		offset;

	offset = inst & 0xFFFF;
	vaddr = frame->f_regs[(inst>>21)&0x1F] + offset;

	/* segment check */
	if (vaddr & 0x80000000) {
		send_sigsegv(vaddr, T_ADDR_ERR_ST, frame, cause);
		return;
	}

	if (copyin((void *)(vaddr & ~0x3), &a, 4) != 0) {
		send_sigsegv(vaddr, T_TLB_ST_MISS, frame, cause);
		return;
	}

	x = frame->f_regs[(inst>>16)&0x1F];

	shift = (vaddr & 0x00000003) * 8;
	x <<= shift;
	a &= ~(0xFFFFFFFFUL << shift);
	a |= x;

	if (suword((void *)vaddr, a) < 0) {
		send_sigsegv(vaddr, T_TLB_ST_MISS, frame, cause);
		return;
	}

	update_pc(frame, cause);
}
#endif /* defined(SOFTFLOAT) */