/*	$NetBSD: fpemu.S,v 1.7 2019/04/15 20:45:08 skrll Exp $	*/

/*	$OpenBSD: fpemu.S,v 1.4 2001/03/29 02:18:45 mickey Exp $	*/

/*
 * Copyright (c) 2000-2004 Michael Shalayeff
 * 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.
 *
 * 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 OR HIS RELATIVES 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 MIND, 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 <machine/asm.h>
#include "assym.h"

#define	FPEMU_VERSION	(1 << 11)

#define	FP_TABLE2(name, ep0, ep1, ep2, ep3) \
	ldil	L%L$fpemu_tbl$name, %t1			! \
	ldo	R%L$fpemu_tbl$name(%t1), %t1		! \
	ldwx,s	%r1(%t1), %t2				! \
	bv	%r0(%t2)				! \
	nop						! \
	.label L$fpemu_tbl$name				! \
	.import	__CONCAT(__CONCAT(ep0,_),name), code	! \
	.import	__CONCAT(__CONCAT(ep1,_),name), code	! \
	.import	__CONCAT(__CONCAT(ep2,_),name), code	! \
	.import	__CONCAT(__CONCAT(ep3,_),name), code	! \
	.word	__CONCAT(__CONCAT(ep0,_),name),	__CONCAT(__CONCAT(ep1,_),name),	__CONCAT(__CONCAT(ep2,_),name),	__CONCAT(__CONCAT(ep3,_),name)

#define	FP_TABLE3(name,ep0,ep1,ep2,ep3,ep4,ep5,ep6,ep7,ep8,ep9,epa,epb,epc,epd,epe,epf) \
	ldil	L%L$fpemu_tbl$name, %t1			! \
	ldo	R%L$fpemu_tbl$name(%t1), %t1		! \
	ldwx,s	%r1(%t1), %t2				! \
	bv	%r0(%t2)				! \
	nop						! \
	.label	L$fpemu_tbl$name				! \
	.import	__CONCAT(__CONCAT(ep0,_),name), code	! \
	.import	__CONCAT(__CONCAT(ep1,_),name), code	! \
	.import	__CONCAT(__CONCAT(ep2,_),name), code	! \
	.import	__CONCAT(__CONCAT(ep3,_),name), code	! \
	.import	__CONCAT(__CONCAT(ep4,_),name), code	! \
	.import	__CONCAT(__CONCAT(ep5,_),name), code	! \
	.import	__CONCAT(__CONCAT(ep6,_),name), code	! \
	.import	__CONCAT(__CONCAT(ep7,_),name), code	! \
	.import	__CONCAT(__CONCAT(ep8,_),name), code	! \
	.import	__CONCAT(__CONCAT(ep9,_),name), code	! \
	.import	__CONCAT(__CONCAT(epa,_),name), code	! \
	.import	__CONCAT(__CONCAT(epb,_),name), code	! \
	.import	__CONCAT(__CONCAT(epc,_),name), code	! \
	.import	__CONCAT(__CONCAT(epd,_),name), code	! \
	.import	__CONCAT(__CONCAT(epe,_),name), code	! \
	.import	__CONCAT(__CONCAT(epf,_),name), code	! \
	.word	__CONCAT(__CONCAT(ep0,_),name),	__CONCAT(__CONCAT(ep1,_),name),	__CONCAT(__CONCAT(ep2,_),name), __CONCAT(__CONCAT(ep3,_),name), __CONCAT(__CONCAT(ep4,_),name), __CONCAT(__CONCAT(ep5,_),name), __CONCAT(__CONCAT(ep6,_),name), __CONCAT(__CONCAT(ep7,_),name), __CONCAT(__CONCAT(ep8,_),name), __CONCAT(__CONCAT(ep9,_),name), __CONCAT(__CONCAT(epa,_),name), __CONCAT(__CONCAT(epb,_),name), __CONCAT(__CONCAT(epc,_),name), __CONCAT(__CONCAT(epd,_),name), __CONCAT(__CONCAT(epe,_),name), __CONCAT(__CONCAT(epf,_),name)

	.section .bss

	.export	L$fpemu_stack, data
L$fpemu_stack:
	.comm	NBPG

	.text
/*
 * fpu_emulate(iir)
 */
LEAF_ENTRY_NOPROFILE(fpu_emulate)

	extru	%arg0, 22, 2, %arg3
	extru	%arg0, 18, 3, %r31
	comib,=	1, %arg3, L$fpu_cln1
	nop
	extru	%arg0, 16, 2, %r31

L$fpu_cln1:
	/*
	 * theoreticaly we would need to determine the fpu instruction
	 * exception type (there could be 4 of those, but stick w/
	 * non-timex fpus for now.
	 */
	ldi	1, %ret0
	extru,<> %arg0, 10, 5, %r1
	ldi	32, %r1	/* fpemu zero reg */
	extru,<> %arg0, 31, 5, %t1
	b,n	L$fpemu_nzt
	comib,=,n 2, %arg3, L$fpemu_exit
L$fpemu_nzt:
	copy	%arg0, %t4
	sh3add	%r1, %arg2, %arg0
	extru	%arg1, 20, 2, %r1
	sh3add	%t1, %arg2, %arg1

	/*
	 * arg0 -- source register (address)
	 * arg1 -- target register (address)
	 * arg2 -- fpregs context
	 * arg3 -- class
	 * r31 -- subop
	 * r1 -- format specifier
	 * (t4 -- copy or arg0, ie iir)
	 */
	comib,=,n 0, %arg3, L$fpemu0c_0
	comib,=,n 1, %arg3, L$fpemu0c_1
	comib,=,n 2, %arg3, L$fpemu0c_2
	comib,=,n 3, %arg3, L$fpemu0c_3

L$fpemu0c_0:
	comib,=,n 0, %r31, L$fpemu0c_0_0
	comib,=,n 1, %r31, L$fpemu_exit
	comib,=,n 2, %r31, L$fpemu0c_0_2
	comib,=,n 3, %r31, L$fpemu0c_0_3
	comib,=,n 4, %r31, L$fpemu0c_0_4
	comib,=,n 5, %r31, L$fpemu0c_0_5
	comib,=,n 6, %r31, L$fpemu_exit
	comib,=,n 7, %r31, L$fpemu_exit

L$fpemu0c_0_0:
	ldi	FPEMU_VERSION, %t4
	stw	%t4, 0(%arg2)
	bv	0(%rp)
	copy	%r0, %ret0

L$fpemu0c_0_2:	/* fcpy */
	comib,=,n 2, %r1, L$fpemu_exit
	subi	3, %r1, %r1
	ldw	0*4(%arg0), %t1
	ldw	1*4(%arg0), %t2
	ldw	2*4(%arg0), %t3
	ldw	3*4(%arg0), %t4
	blr,n	%r1, %r0
	nop
	stw	%t3, 2*4(%arg1)
	stw	%t4, 3*4(%arg1)
	stw	%t2, 1*4(%arg1)
	nop
	nop
	nop
	stw	%t1, 0*4(%arg1)
	bv	0(%rp)
	copy	%r0, %ret0

L$fpemu0c_0_3:	/* fabs */
	comib,=,n 2, %r1, L$fpemu_exit
	subi	3, %r1, %r1
	ldw	0*4(%arg0), %t1
	ldw	1*4(%arg0), %t2
	ldw	2*4(%arg0), %t3
	ldw	3*4(%arg0), %t4
	depi	0, 0, 1, %t1
	blr,n	%r1, %r0
	nop
	stw	%t3, 2*4(%arg1)
	stw	%t4, 3*4(%arg1)
	stw	%t2, 1*4(%arg1)
	nop
	nop
	nop
	stw	%t1, 0*4(%arg1)
	bv	0(%rp)
	copy	%r0, %ret0

L$fpemu0c_0_4:	/* fsqrt */
	/* quad not implemented */
	FP_TABLE2(fsqrt,sgl,dbl,invalid,invalid)

L$fpemu0c_0_5:	/* frnd */
	/* quad not implemented */
	FP_TABLE2(frnd,sgl,dbl,invalid,quad)

L$fpemu0c_1:
	extru	%t4, 18, 2, %t2
	sh2add	%r1, %t2, %r1
	comib,=,n 0, %r31, L$fpemu0c_1_0
	comib,=,n 1, %r31, L$fpemu0c_1_1
	comib,=,n 2, %r31, L$fpemu0c_1_2
	comib,=,n 3, %r31, L$fpemu0c_1_3

L$fpemu0c_1_0:	/* fcnvff */
#define	sgl_to_quad_fcnvff	invalid_fcnvff
#define	dbl_to_quad_fcnvff	invalid_fcnvff
#define	quad_to_sgl_fcnvff	invalid_fcnvff
#define	quad_to_dbl_fcnvff	invalid_fcnvff
	FP_TABLE3(fcnvff, invalid, sgl_to_dbl, invalid, sgl_to_quad, dbl_to_sgl, invalid, invalid, dbl_to_quad, invalid, invalid, invalid, invalid, quad_to_sgl, quad_to_dbl, invalid, invalid)

L$fpemu0c_1_1:	/* fcnvxf */
#define	sgl_to_quad_fcnvxf	invalid_fcnvxf
#define	dbl_to_quad_fcnvxf	invalid_fcnvxf
#define	quad_to_sgl_fcnvxf	invalid_fcnvxf
#define	quad_to_dbl_fcnvxf	invalid_fcnvxf
#define	quad_to_quad_fcnvxf	invalid_fcnvxf
	FP_TABLE3(fcnvxf, sgl_to_sgl, sgl_to_dbl, invalid, sgl_to_quad, dbl_to_sgl, dbl_to_dbl, invalid, dbl_to_quad, invalid, invalid, invalid, invalid, quad_to_sgl, quad_to_dbl, invalid, quad_to_quad)

L$fpemu0c_1_2:	/* fcnvfx */
#define	sgl_to_quad_fcnvfx	invalid_fcnvfx
#define	dbl_to_quad_fcnvfx	invalid_fcnvfx
#define	quad_to_sgl_fcnvfx	invalid_fcnvfx
#define	quad_to_dbl_fcnvfx	invalid_fcnvfx
#define	quad_to_quad_fcnvfx	invalid_fcnvfx
	FP_TABLE3(fcnvfx, sgl_to_sgl, sgl_to_dbl, invalid, sgl_to_quad, dbl_to_sgl, dbl_to_dbl, invalid, dbl_to_quad, invalid, invalid, invalid, invalid, quad_to_sgl, quad_to_dbl, invalid, quad_to_quad)

L$fpemu0c_1_3:	/* fcnvfxt */
#define	sgl_to_quad_fcnvfxt	invalid_fcnvfxt
#define	dbl_to_quad_fcnvfxt	invalid_fcnvfxt
#define	quad_to_sgl_fcnvfxt	invalid_fcnvfxt
#define	quad_to_dbl_fcnvfxt	invalid_fcnvfxt
#define	quad_to_quad_fcnvfxt	invalid_fcnvfxt
	FP_TABLE3(fcnvfxt, sgl_to_sgl, sgl_to_dbl, invalid, sgl_to_quad, dbl_to_sgl, dbl_to_dbl, invalid, dbl_to_quad, invalid, invalid, invalid, invalid, quad_to_sgl, quad_to_dbl, invalid, quad_to_quad)


L$fpemu0c_2:
	comib,=,n 0, %r31, L$fpemu0c_2_0
	comib,=,n 1, %r31, L$fpemu0c_2_1
	comib,=,n 2, %r31, L$fpemu_exit
	comib,=,n 3, %r31, L$fpemu_exit
	comib,=,n 4, %r31, L$fpemu_exit
	comib,=,n 5, %r31, L$fpemu_exit
	comib,=,n 6, %r31, L$fpemu_exit
	comib,=,n 7, %r31, L$fpemu_exit

L$fpemu0c_2_0:
	copy	%arg2, %arg3
	extru,<> %t4, 15, 5, %t1
	ldi	32, %t1
	sh3add	%t1, %arg3, %arg1
	extru	%t4, 31, 5, %arg2
	FP_TABLE2(fcmp,sgl,dbl,invalid,invalid)

L$fpemu0c_2_1:
	comib,<>,n 0, %r1, L$fpemu_exit

	/* extru	%t4, 31, 5, %arg1 */
	/* XXX timex is much more compilicated */
	ldw	0(%arg2), %t1
	ldi	0, %ret0
	extru,<> %t1, 5, 1, %r0
	bv,n	%r0(%rp)

	/* advance the pcqueue */
	mtctl	%r0, %pcsq
	mfctl	%pcsq, %t2
	mtctl	%t2, %pcsq
	mtctl	%t2, %pcsq
	mtctl	%r0, %pcoq
	mfctl	%pcoq, %t2
	mtctl	%t2, %pcoq
	ldo	4(%t2), %t2
	bv	%r0(%rp)
	mtctl	%t2, %pcoq

L$fpemu0c_3:
	copy	%arg2, %arg3
	extru,<> %t4, 31, 5, %t1
	ldi	32, %t1
	sh3add	%t1, %arg3, %arg2

	comib,=,n 0, %r31, L$fpemu0c_3_0
	comib,=,n 1, %r31, L$fpemu0c_3_1
	comib,=,n 2, %r31, L$fpemu0c_3_2
	comib,=,n 3, %r31, L$fpemu0c_3_3
	comib,=,n 4, %r31, L$fpemu0c_3_4
	comib,=,n 5, %r31, L$fpemu_exit
	comib,=,n 6, %r31, L$fpemu_exit
	comib,=,n 7, %r31, L$fpemu_exit

L$fpemu0c_3_0:	/* fadd */
	FP_TABLE2(fadd,sgl,dbl,invalid,invalid)

L$fpemu0c_3_1:	/* fsub */
	FP_TABLE2(fsub,sgl,dbl,invalid,invalid)

L$fpemu0c_3_2:	/* fmpy */
	FP_TABLE2(fmpy,sgl,dbl,invalid,invalid)

L$fpemu0c_3_3:	/* fdiv */
	FP_TABLE2(fdiv,sgl,dbl,invalid,invalid)

L$fpemu0c_3_4:	/* frem */
	FP_TABLE2(frem,sgl,dbl,invalid,invalid)

	.export	L$fpemu_exit, code
L$fpemu_exit:
	/* these look very ugly, but we don't want to mess up w/ m4 just
	 * for the sake of overall world prettieness value growth XXX */
invalid_fsqrt:
invalid_frnd:
invalid_fcnvff:
invalid_fcnvxf:
invalid_fcnvfx:
invalid_fcnvfxt:
invalid_fcmp:
invalid_fadd:
invalid_fsub:
invalid_fmpy:
invalid_fdiv:
invalid_frem:
	bv,n	0(%rp)
EXIT(fpu_emulate)


	.end

