root/sys/arch/hppa/hppa/fpemu.S
/*      $NetBSD: fpemu.S,v 1.8 2024/09/07 06:17:37 andvar 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:
        /*
         * theoretically 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