root/sys/arch/mips/mips/bds_emul.S
/*      $NetBSD: bds_emul.S,v 1.11 2026/03/12 05:16:32 andvar Exp $     */

/*
 * Copyright (c) 1992, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Ralph Campbell.
 *
 * 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. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
 *
 *      @(#)fp.s        8.1 (Berkeley) 6/10/93
 */

#include <sys/cdefs.h>

#include <mips/asm.h>
#include <mips/trap.h>                  /* for T_RES_INST */
#include <mips/cpuregs.h>

#include "assym.h"

/* insns are reordered in the way as MIPS architecture imposes */
        .set    reorder

#define REG_REGMASK     (0x1f << REG_SCALESHIFT)

/*
 * Emulate CPU instruction in branch delay slot.
 * These instructions are not implemented and causes SIGILL.
 *  jump/branch
 *  COP0
 *  trap/syscall/break
 *
 * Args are same as mips_emul_fp, eg.
 * void mips_emul_delayslot(uint32_t insn, struct trapframe *tf, uint32_t cause);
 * It should be used to emulate an instruction in a branch delay slot.
 */
LEAF(mips_emul_branchdelayslot)
        REG_PROLOGUE
        REG_S   zero, TF_REG_ZERO(a1)           # ensure zero has value 0
        REG_EPILOGUE
        move    t3, ra                          # need to save this locally

        srl     t0, a0, 26-PTR_SCALESHIFT
        andi    t0, 0x3F << PTR_SCALESHIFT
        PTR_L   t9, bcemul_optbl(t0)
        jr      t9

        .rdata
bcemul_optbl:
        PTR_WORD bcemul_special                 # 000 SPECIAL
        PTR_WORD bcemul_sigill                  # 001 REGIMM
        PTR_WORD bcemul_sigill                  # 002 J
        PTR_WORD bcemul_sigill                  # 003 JAL
        PTR_WORD bcemul_sigill                  # 004 BEQ
        PTR_WORD bcemul_sigill                  # 005 BNE
        PTR_WORD bcemul_sigill                  # 006 BLEZ
        PTR_WORD bcemul_sigill                  # 007 BGTZ
        PTR_WORD bcemul_addi                    # 010 ADDI
        PTR_WORD bcemul_addiu                   # 011 ADDIU
        PTR_WORD bcemul_slti                    # 012 SLTI
        PTR_WORD bcemul_sltiu                   # 013 SLTIU
        PTR_WORD bcemul_andi                    # 014 ANDI
        PTR_WORD bcemul_ori                     # 015 ORI
        PTR_WORD bcemul_xori                    # 016 XORI
        PTR_WORD bcemul_lui                     # 017 LUI
        PTR_WORD bcemul_sigill                  # 020 COP0
        PTR_WORD _C_LABEL(mips_emul_fp)         # 021 COP1
        PTR_WORD bcemul_sigill                  # 022 COP2
        PTR_WORD bcemul_sigill                  # 023 COP1X
        PTR_WORD bcemul_sigill                  # 024 BEQL
        PTR_WORD bcemul_sigill                  # 025 BNEL
        PTR_WORD bcemul_sigill                  # 026 BNEZL
        PTR_WORD bcemul_sigill                  # 027 BGTZL
#ifdef __mips_o32
        PTR_WORD bcemul_sigill                  # 030 DADDI (*)
        PTR_WORD bcemul_sigill                  # 031 DADDIU (*)
        PTR_WORD bcemul_sigill                  # 030 LDL (*)
        PTR_WORD bcemul_sigill                  # 031 LDR (*)
#else
        PTR_WORD bcemul_daddi                   # 030 DADDI (*)
        PTR_WORD bcemul_daddiu                  # 031 DADDIU (*)
        PTR_WORD _C_LABEL(mips_emul_ldl)        # 032 LDL (*)
        PTR_WORD _C_LABEL(mips_emul_ldr)        # 033 LDR (*)
#endif
        PTR_WORD bcemul_sigill                  # 034 SPECIAL2
        PTR_WORD bcemul_sigill                  # 035 JALX
        PTR_WORD bcemul_sigill                  # 036 MDMX
        PTR_WORD bcemul_sigill                  # 037 SPECIAL3
        PTR_WORD _C_LABEL(mips_emul_lb)         # 040 LB
        PTR_WORD _C_LABEL(mips_emul_lh)         # 041 LH
        PTR_WORD _C_LABEL(mips_emul_lwl)        # 042 LWL
        PTR_WORD _C_LABEL(mips_emul_lw)         # 043 LW
        PTR_WORD _C_LABEL(mips_emul_lbu)        # 044 LBU
        PTR_WORD _C_LABEL(mips_emul_lhu)        # 045 LHU
        PTR_WORD _C_LABEL(mips_emul_lwr)        # 046 LWR
#ifdef __mips_o32
        PTR_WORD bcemul_sigill                  # 047 LWU (*)
#else
        PTR_WORD _C_LABEL(mips_emul_lwu)        # 047 LWU (*)
#endif
        PTR_WORD _C_LABEL(mips_emul_sb)         # 050 SB
        PTR_WORD _C_LABEL(mips_emul_sh)         # 051 SH
        PTR_WORD _C_LABEL(mips_emul_swl)        # 052 SWL
        PTR_WORD _C_LABEL(mips_emul_sw)         # 053 SW
#ifdef __mips_o32
        PTR_WORD bcemul_sigill                  # 054 SDL (*)
        PTR_WORD bcemul_sigill                  # 055 SDR (*)
#else
        PTR_WORD _C_LABEL(mips_emul_sdl)        # 054 SDL (*)
        PTR_WORD _C_LABEL(mips_emul_sdr)        # 055 SDR (*)
#endif
        PTR_WORD _C_LABEL(mips_emul_swr)        # 056 SWR
        PTR_WORD bcemul_sigill                  # 057 CACHE
        PTR_WORD bcemul_sigill                  # 060 LL
        PTR_WORD _C_LABEL(mips_emul_lwc1)       # 061 LWC1
        PTR_WORD bcemul_sigill                  # 062 LWC2
        PTR_WORD bcemul_sigill                  # 063 PREF
        PTR_WORD bcemul_sigill                  # 064 LLD (*)
        PTR_WORD _C_LABEL(mips_emul_ldc1)       # 065 LDC
        PTR_WORD bcemul_sigill                  # 066 LDC2
#ifdef __mips_o32
        PTR_WORD bcemul_sigill                  # 067 LD (*)
#else
        PTR_WORD _C_LABEL(mips_emul_ld)         # 067 LD (*)
#endif
        PTR_WORD bcemul_sigill                  # 070 SC
        PTR_WORD _C_LABEL(mips_emul_swc1)       # 071 SWC1
        PTR_WORD bcemul_sigill                  # 072 SWC2
        PTR_WORD bcemul_sigill                  # 073
        PTR_WORD bcemul_sigill                  # 074 SCD (*)
        PTR_WORD _C_LABEL(mips_emul_sdc1)       # 075 SDC1
        PTR_WORD bcemul_sigill                  # 076 SDC2
#ifdef __mips_o32
        PTR_WORD bcemul_sigill                  # 077 SD (*)
#else
        PTR_WORD _C_LABEL(mips_emul_sd)         # 077 SD (*)
#endif

        .text
bcemul_immed_prologue:
        srl     t1, a0, 21-REG_SCALESHIFT       # rs (source)
        srl     t2, a0, 16-REG_SCALESHIFT       # rt (dest)
        andi    t1, REG_REGMASK
        andi    t2, REG_REGMASK
        PTR_ADDU t1, a1
        PTR_ADDU t2, a1
        sll     v1, a0, 16
        sra     v1, v1, 16                      # signed immed
        REG_PROLOGUE
        REG_L   v0, TF_REG_ZERO(t1)             # load source
        REG_EPILOGUE
        jr      ra                              # execute the instruction

bcemul_uimmed_prologue:
        srl     t1, a0, 21-REG_SCALESHIFT       # rs (source)
        srl     t2, a0, 16-REG_SCALESHIFT       # rt (dest)
        andi    t1, REG_REGMASK
        andi    t2, REG_REGMASK
        PTR_ADDU t1, a1
        PTR_ADDU t2, a1
        andi    v1, a0, 0xffff                  # unsigned immed
        REG_PROLOGUE
        REG_L   v0, TF_REG_ZERO(t1)             # load source
        REG_EPILOGUE
        jr      ra

#ifndef __mips_o32
bcemul_daddi:
        bal     bcemul_immed_prologue
        dadd    t0, v0, v1
        b       bcemul_check_add_overflow
#endif

bcemul_addi:
        bal     bcemul_immed_prologue
        addu    t0, v0, v1

bcemul_check_add_overflow:
        move    ta0, t0         # result
        move    ta1, v0         # addend 1
        move    ta2, v1         # addend 2

bcemul_check_overflow:
        /* Overflow check */
        xor     ta2, ta1        /* negative + positive can't overflow */
        bltz    ta2, bcemul_store_and_done

        /* must have added two positive or two negative numbers */
        xor     ta1, ta0        /* result have same signedness as source? */
        bgez    ta1, bcemul_store_and_done /* yes, then we didn't overflow */

        b       bcemul_sigill

bcemul_addiu:
        bal     bcemul_immed_prologue
        addu    t0, v0, v1
        b       bcemul_store_and_done

#ifndef __mips_o32
bcemul_daddiu:
        bal     bcemul_immed_prologue
        daddu   t0, v0, v1
        b       bcemul_store_and_done
#endif

bcemul_slti:
        bal     bcemul_immed_prologue
        slt     t0, v0, v1
        b       bcemul_store_and_done

bcemul_sltiu:
        bal     bcemul_immed_prologue
        sltu    t0, v0, v1
        b       bcemul_store_and_done

bcemul_andi:
        bal     bcemul_uimmed_prologue
        and     t0, v0, v1
        b       bcemul_store_and_done

bcemul_ori:
        bal     bcemul_uimmed_prologue
        or      t0, v0, v1
        b       bcemul_store_and_done

bcemul_xori:
        bal     bcemul_uimmed_prologue
        xor     t0, v0, v1
        b       bcemul_store_and_done

bcemul_lui:
        srl     t2, a0, 16-REG_SCALESHIFT       # rt
        andi    t2, REG_REGMASK
        PTR_ADDU t2, a1
        sll     t0, a0, 16
        b       bcemul_store_and_done

        .rdata
bcemul_specialtbl:
        PTR_WORD bcemul_special_genshift        # 000 SLL
        PTR_WORD bcemul_sigill                  # 001 MOVC1
        PTR_WORD bcemul_special_genshift        # 002 SRL
        PTR_WORD bcemul_special_genshift        # 003 SRA
        PTR_WORD bcemul_special_genshiftv       # 004 SLLV
        PTR_WORD bcemul_sigill                  # 005
        PTR_WORD bcemul_special_genshiftv       # 006 SRLV
        PTR_WORD bcemul_special_genshiftv       # 007 SRAV
        PTR_WORD bcemul_sigill                  # 010 JR
        PTR_WORD bcemul_sigill                  # 011 JALR
        PTR_WORD bcemul_sigill                  # 012 MOVZ
        PTR_WORD bcemul_sigill                  # 013 MOVN
        PTR_WORD bcemul_sigill                  # 014 SYSCALL
        PTR_WORD bcemul_sigill                  # 015 BREAK
        PTR_WORD bcemul_sigill                  # 016
        PTR_WORD bcemul_special_sync            # 017 SYNC
        PTR_WORD bcemul_special_mfhilo          # 020 MFHI
        PTR_WORD bcemul_special_mthilo          # 021 MTHI
        PTR_WORD bcemul_special_mfhilo          # 022 MFLO
        PTR_WORD bcemul_special_mthilo          # 023 MTLO
#ifdef __mips_o32
        PTR_WORD bcemul_sigill                  # 024 DSLLV (*)
        PTR_WORD bcemul_sigill                  # 025
        PTR_WORD bcemul_sigill                  # 026 DSRLV (*)
        PTR_WORD bcemul_sigill                  # 027 DSRAV (*)
#else
        PTR_WORD bcemul_special_genshiftv       # 024 DSLLV (*)
        PTR_WORD bcemul_sigill                  # 025
        PTR_WORD bcemul_special_genshiftv       # 026 DSRLV (*)
        PTR_WORD bcemul_special_genshiftv       # 027 DSRAV (*)
#endif
        PTR_WORD bcemul_special_genmultdiv      # 030 MULT
        PTR_WORD bcemul_special_genmultdiv      # 031 MULTU
        PTR_WORD bcemul_special_genmultdiv      # 032 DIV
        PTR_WORD bcemul_special_genmultdiv      # 033 DIVU
#ifdef __mips_o32
        PTR_WORD bcemul_sigill                  # 034 DMULT (*)
        PTR_WORD bcemul_sigill                  # 035 DMULTU (*)
        PTR_WORD bcemul_sigill                  # 036 DDIV (*)
        PTR_WORD bcemul_sigill                  # 037 DDIVU (*)
#else
        PTR_WORD bcemul_special_genmultdiv      # 034 DMULT (*)
        PTR_WORD bcemul_special_genmultdiv      # 035 DMULTU (*)
        PTR_WORD bcemul_special_genmultdiv      # 036 DDIV (*)
        PTR_WORD bcemul_special_genmultdiv      # 037 DDIVU (*)
#endif
        PTR_WORD bcemul_special_genadd          # 040 ADD
        PTR_WORD bcemul_special_gen3op          # 041 ADDU
        PTR_WORD bcemul_special_gensub          # 042 SUB
        PTR_WORD bcemul_special_gen3op          # 043 SUBU
        PTR_WORD bcemul_special_gen3op          # 044 AND
        PTR_WORD bcemul_special_gen3op          # 045 OR
        PTR_WORD bcemul_special_gen3op          # 046 XOR
        PTR_WORD bcemul_special_gen3op          # 047 NOR
        PTR_WORD bcemul_sigill                  # 050
        PTR_WORD bcemul_sigill                  # 051
        PTR_WORD bcemul_special_gen3op          # 052 SLT
        PTR_WORD bcemul_special_gen3op          # 053 SLTU
#ifdef __mips_o32
        PTR_WORD bcemul_sigill                  # 054 DADD (*)
        PTR_WORD bcemul_sigill                  # 055 DADDU (*)
        PTR_WORD bcemul_sigill                  # 056 DSUB (*)
        PTR_WORD bcemul_sigill                  # 057 DSUBU (*)
#else
        PTR_WORD bcemul_special_genadd          # 054 DADD (*)
        PTR_WORD bcemul_special_gen3op          # 055 DADDU (*)
        PTR_WORD bcemul_special_gensub          # 056 DSUB (*)
        PTR_WORD bcemul_special_gen3op          # 057 DSUBU (*)
#endif
        PTR_WORD bcemul_sigill                  # 060 TGE
        PTR_WORD bcemul_sigill                  # 061 TGEU
        PTR_WORD bcemul_sigill                  # 062 TLT
        PTR_WORD bcemul_sigill                  # 063 TLTU
        PTR_WORD bcemul_sigill                  # 064 TEQ
        PTR_WORD bcemul_sigill                  # 065
        PTR_WORD bcemul_sigill                  # 066 TNE
        PTR_WORD bcemul_sigill                  # 067
#ifdef __mips_o32
        PTR_WORD bcemul_sigill                  # 070 DSLL (*)
        PTR_WORD bcemul_sigill                  # 071
        PTR_WORD bcemul_sigill                  # 072 DSRL (*)
        PTR_WORD bcemul_sigill                  # 073 DSRA (*)
        PTR_WORD bcemul_sigill                  # 074 DSLL32 (*)
        PTR_WORD bcemul_sigill                  # 075
        PTR_WORD bcemul_sigill                  # 076 DSRL32 (*)
        PTR_WORD bcemul_sigill                  # 077 DSRA32 (*)
#else
        PTR_WORD bcemul_special_genshift        # 070 DSLL (*)
        PTR_WORD bcemul_sigill                  # 071
        PTR_WORD bcemul_special_genshift        # 072 DSRL (*)
        PTR_WORD bcemul_special_genshift        # 073 DSRA (*)
        PTR_WORD bcemul_special_genshift        # 074 DSLL32 (*)
        PTR_WORD bcemul_sigill                  # 075
        PTR_WORD bcemul_special_genshift        # 076 DSRL32 (*)
        PTR_WORD bcemul_special_genshift        # 077 DSRA32 (*)
#endif

        .text
        .set push
        .set noreorder
        .set nomacro
        .set mips64
bcemul_special_op:
        jr      ra;     sllv    t0, v0, v1      # 000 SLL
        jr      ra;     nop                     # 001 MOVC1
        jr      ra;     srlv    t0, v0, v1      # 002 SRL
        jr      ra;     srav    t0, v0, v1      # 003 SRA
        jr      ra;     sllv    t0, v0, v1      # 004 SLLV
        jr      ra;     nop                     # 005 *
        jr      ra;     srlv    t0, v0, v1      # 006 SRLV
        jr      ra;     srav    t0, v0, v1      # 007 SRAV
 
        jr      ra;     nop                     # 010 JR
        jr      ra;     nop                     # 011 JALR
        jr      ra;     movz    t0, v0, v1      # 012 MOVZ
        jr      ra;     movn    t0, v0, v1      # 013 MOVN
        jr      ra;     nop                     # 014 SYSCALL
        jr      ra;     nop                     # 015 BREAK
        jr      ra;     nop                     # 016 *
        jr      ra;     nop                     # 017 SYNC

        jr      ra;     mfhi    t0              # 020 MFHI
        jr      ra;     mthi    v0              # 021 MTHI
        jr      ra;     mflo    t0              # 022 MFLO
        jr      ra;     mtlo    v0              # 023 MTLO
        jr      ra;     dsllv   t0, v0, v1      # 024 DSLLV
        jr      ra;     nop                     # 025 *
        jr      ra;     dsrlv   t0, v0, v1      # 026 DSRLV
        jr      ra;     dsrav   t0, v0, v1      # 027 DSRAV

        jr      ra;     mult    v0, v1          # 030 MULT
        jr      ra;     multu   v0, v1          # 031 MULTU
        jr      ra;     div     $0, v0, v1      # 032 DIV
        jr      ra;     divu    $0, v0, v1      # 033 DIVU
        jr      ra;     dmult   v0, v1          # 034 DMULT
        jr      ra;     dmultu  v0, v1          # 035 DMULTU
        jr      ra;     ddiv    $0, v0, v1      # 036 DDIV
        jr      ra;     ddivu   $0, v0, v1      # 037 DDIVU

        jr      ra;     add     t0, v0, v1      # 040 ADD
        jr      ra;     addu    t0, v0, v1      # 041 ADDU
        jr      ra;     sub     t0, v0, v1      # 042 SUB
        jr      ra;     subu    t0, v0, v1      # 043 SUBU
        jr      ra;     and     t0, v0, v1      # 044 AND
        jr      ra;     or      t0, v0, v1      # 045 OR
        jr      ra;     xor     t0, v0, v1      # 046 XOR
        jr      ra;     nor     t0, v0, v1      # 047 NOR

        jr      ra;     nop                     # 050 *
        jr      ra;     nop                     # 051 *
        jr      ra;     slt     t0, v0, v1      # 052 SLT
        jr      ra;     sltu    t0, v0, v1      # 053 SLTU
        jr      ra;     dadd    t0, v0, v1      # 054 DADD
        jr      ra;     daddu   t0, v0, v1      # 055 DADDU
        jr      ra;     dsub    t0, v0, v1      # 056 DSUB
        jr      ra;     dsubu   t0, v0, v1      # 057 DSUBU

        jr      ra;     tge     v0, v1          # 060 TGE
        jr      ra;     tgeu    v0, v1          # 061 TGEU
        jr      ra;     tlt     v0, v1          # 062 TLT
        jr      ra;     tltu    v0, v1          # 063 TLTU
        jr      ra;     teq     v0, v1          # 064 TEQ
        jr      ra;     nop                     # 065 *
        jr      ra;     tne     v0, v1          # 066 TNE
        jr      ra;     nop                     # 067 *

        jr      ra;     dsllv   t0, v0, v1      # 070 DSLL
        jr      ra;     nop                     # 071 *
        jr      ra;     dsrlv   t0, v0, v1      # 072 DSRL
        jr      ra;     dsrav   t0, v0, v1      # 073 DSRA
        jr      ra;     dsllv   t0, v0, v1      # 074 DSLL32
        jr      ra;     nop                     # 075 *
        jr      ra;     dsrlv   t0, v0, v1      # 076 DSRL32
        jr      ra;     dsrav   t0, v0, v1      # 077 DSRA32
        .set pop        

bcemul_special:
        andi    t0, a0, 0x3f                    # get special code
        sll     t1, t0, 3                       # calculate index in specialop
        sll     t0, PTR_SCALESHIFT
        PTR_L   t9, bcemul_specialtbl(t0)
        PTR_LA  t0, bcemul_special_op(t1)
        jr      t9

bcemul_special_3op_prologue:
        move    t9, t0
        srl     t0, a0, 21-REG_SCALESHIFT       # rs (source1)
        srl     t1, a0, 16-REG_SCALESHIFT       # rt (source2)
        srl     t2, a0, 11-REG_SCALESHIFT       # rd (dest)
        andi    t0, REG_REGMASK
        andi    t1, REG_REGMASK
        andi    t2, REG_REGMASK
        PTR_ADDU t0, a1
        PTR_ADDU t1, a1
        PTR_ADDU t2, a1
        REG_PROLOGUE
        REG_L   v0, TF_REG_ZERO(t0)             # load source1
        REG_L   v1, TF_REG_ZERO(t1)             # load source2
        REG_EPILOGUE
        jr      t9

bcemul_special_2src_prologue:
        move    t9, t0
        srl     t0, a0, 21-REG_SCALESHIFT       # rs (source1)
        srl     t1, a0, 16-REG_SCALESHIFT       # rt (source2)
        andi    t0, REG_REGMASK
        andi    t1, REG_REGMASK
        PTR_ADDU t0, a1
        PTR_ADDU t1, a1
        REG_PROLOGUE
        REG_L   v0, TF_REG_ZERO(t0)             # load source1
        REG_L   v1, TF_REG_ZERO(t1)             # load source2
        REG_EPILOGUE
        jr      t9

bcemul_special_genshift:
        move    t9, t0
        srl     t1, a0, 16-REG_SCALESHIFT       # rt (source)
        srl     t2, a0, 11-REG_SCALESHIFT       # rd (dest)
        srl     v1, a0, 6                       # sa
        andi    t1, REG_REGMASK
        andi    t2, REG_REGMASK
        beqz    t2, bcemul_done                 # fast escape for nop variants
        andi    v1, 0x001F
#ifndef __mips_o32
        /*
         * Deal with DSLL32, DSRA32, DSRL32 who need 32 added to their
         * shift count.
         */
        andi    v0, a0, 077                     # focus on special op
        sltiu   v0, v0, 074                     # less than DSLL32?
        xori    v0, 1                           # invert result
        sll     v0, 5                           # shift by 5 (now 0 or 32)
        addu    v1, v0                          # add to shift count
#endif
        PTR_ADDU t1, a1
        PTR_ADDU t2, a1
        REG_PROLOGUE
        REG_L   v0, TF_REG_ZERO(t1)             # load source
        REG_EPILOGUE
        jal     t9
        j       bcemul_store_and_done

bcemul_special_sync:
        b       bcemul_done

bcemul_special_mfhilo:
        srl     t2, a0, 11-REG_SCALESHIFT       # rd (dest)
        andi    t2, REG_REGMASK
        PTR_ADDU t1, a1, TF_REG_MULHI
        PTR_ADDU t2, a1
        and     v0, a0, 2                       # bit 1: 0=HI 1=LO
        sll     v0, REG_SCALESHIFT - 1
#if TF_REG_MULLO < TF_REG_MULHI
        PTR_SUBU t1, v0
#else
        PTR_ADDU t1, v0
#endif
        REG_PROLOGUE
        REG_L   t0, TF_REG_ZERO(t1)             # load source (mullo or mulhi)
        REG_EPILOGUE
        b       bcemul_store_and_done

bcemul_special_mthilo:
        srl     t1, a0, 21-REG_SCALESHIFT       # rs (source)
        andi    t1, REG_REGMASK
        PTR_ADDU t1, a1
        PTR_ADDU t2, a1, TF_REG_MULHI
        and     v0, a0, 2                       # bit 1: 0=HI l=LO
        sll     v0, REG_SCALESHIFT - 1
#if TF_REG_MULLO < TF_REG_MULHI
        PTR_SUBU t2, v0 
#else
        PTR_ADDU t2, v0
#endif
        REG_PROLOGUE
        REG_L   t0, TF_REG_ZERO(t1)
        REG_EPILOGUE
        b       bcemul_store_and_done

bcemul_special_genmultdiv:
        bal     bcemul_special_2src_prologue    # grab operands and do it.
        mflo    v0
        mfhi    v1
        REG_PROLOGUE
        REG_S   v0, TF_REG_MULLO(a1)
        REG_S   v1, TF_REG_MULHI(a1)
        REG_EPILOGUE
        b       bcemul_done

bcemul_special_genadd:
        bal     bcemul_special_3op_prologue
        b       bcemul_check_add_overflow

bcemul_special_gensub:
        bal     bcemul_special_3op_prologue

        /*
         * t0 = v0 - v1 which means v0 = t0 + v1
         * Now we can the same logic for overflow from addition.
         */
        move    ta0, v0
        move    ta1, t0
        move    ta2, v1

        b       bcemul_check_overflow

bcemul_special_genshiftv:
bcemul_special_gen3op:
        bal     bcemul_special_3op_prologue
        #b      bcemul_store_and_done   # fallthrough to bcemul_store_and_done

bcemul_store_and_done:
        beq     a1, t2, bcemul_done     # don't store to zero
        REG_PROLOGUE
        REG_S   t0, TF_REG_ZERO(t2)
        REG_EPILOGUE
        #b      bcemul_done             # fallthrough to bcemul_done

bcemul_done:
/*
 * Succeeded to emulate instruction with no error
 * so compute the next PC.
 */
        PTR_SUBU sp, CALLFRAME_SIZ
        REG_S   t3, CALLFRAME_RA(sp)
        REG_S   s0, CALLFRAME_S0(sp)
        move    s0, a1                  # save trapframe

        /* Fetch previous branch instruction */
        REG_PROLOGUE
        REG_L   a0, TF_REG_EPC(s0)
        REG_EPILOGUE
        jal     _C_LABEL(mips_ufetch32)

        /* Calculate branch destination */
        sll     t0, v0, 16
        sra     t0, t0, 16-2
        REG_PROLOGUE
        REG_L   t1, TF_REG_EPC(s0)
        PTR_ADDU t0, 4
        PTR_ADDU t1, t0
        REG_S   t1, TF_REG_EPC(s0)
        REG_EPILOGUE

        REG_L   ra, CALLFRAME_RA(sp)
        REG_L   s0, CALLFRAME_S0(sp)
        PTR_ADDU sp, CALLFRAME_SIZ
        jr      ra

/*
 * Send SIGILL, SIGFPE.
 * Args are same as mips_emul_fp.
 */
bcemul_sigill:
        li      t0, 0xFFFFFF00
        and     a2, a2, t0
        ori     a2, a2, T_RES_INST << MIPS_CR_EXC_CODE_SHIFT
        REG_PROLOGUE
        REG_S   a2, TF_REG_CAUSE(a1)
        REG_EPILOGUE

        move    ra, t3                          # restore ra
        move    a1, a0                          # code = instruction
        move    a0, MIPS_CURLWP                 # get current process
        j       _C_LABEL(mips_fpuillinst)

END(mips_emul_branchdelayslot)