/* Simulator for Analog Devices Blackfin processors. Copyright (C) 2005-2011 Free Software Foundation, Inc. Contributed by Analog Devices, Inc. This file is part of simulators. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "config.h" #include #include #include #include #include "opcode/bfin.h" #include "sim-main.h" #include "dv-bfin_cec.h" #include "dv-bfin_mmu.h" #define HOST_LONG_WORD_SIZE (sizeof (long) * 8) #define SIGNEXTEND(v, n) \ (((bs32)(v) << (HOST_LONG_WORD_SIZE - (n))) >> (HOST_LONG_WORD_SIZE - (n))) static __attribute__ ((noreturn)) void illegal_instruction (SIM_CPU *cpu) { TRACE_INSN (cpu, "ILLEGAL INSTRUCTION"); while (1) cec_exception (cpu, VEC_UNDEF_I); } static __attribute__ ((noreturn)) void illegal_instruction_combination (SIM_CPU *cpu) { TRACE_INSN (cpu, "ILLEGAL INSTRUCTION COMBINATION"); while (1) cec_exception (cpu, VEC_ILGAL_I); } static __attribute__ ((noreturn)) void unhandled_instruction (SIM_CPU *cpu, const char *insn) { SIM_DESC sd = CPU_STATE (cpu); bu16 iw0, iw1; bu32 iw2; TRACE_EVENTS (cpu, "unhandled instruction"); iw0 = IFETCH (PCREG); iw1 = IFETCH (PCREG + 2); iw2 = ((bu32)iw0 << 16) | iw1; sim_io_eprintf (sd, "Unhandled instruction at 0x%08x (%s opcode 0x", PCREG, insn); if ((iw0 & 0xc000) == 0xc000) sim_io_eprintf (sd, "%08x", iw2); else sim_io_eprintf (sd, "%04x", iw0); sim_io_eprintf (sd, ") ... aborting\n"); illegal_instruction (cpu); } typedef enum { c_0, c_1, c_4, c_2, c_uimm2, c_uimm3, c_imm3, c_pcrel4, c_imm4, c_uimm4s4, c_uimm4s4d, c_uimm4, c_uimm4s2, c_negimm5s4, c_imm5, c_imm5d, c_uimm5, c_imm6, c_imm7, c_imm7d, c_imm8, c_uimm8, c_pcrel8, c_uimm8s4, c_pcrel8s4, c_lppcrel10, c_pcrel10, c_pcrel12, c_imm16s4, c_luimm16, c_imm16, c_imm16d, c_huimm16, c_rimm16, c_imm16s2, c_uimm16s4, c_uimm16s4d, c_uimm16, c_pcrel24, c_uimm32, c_imm32, c_huimm32, c_huimm32e, } const_forms_t; static const struct { const char *name; const int nbits; const char reloc; const char issigned; const char pcrel; const char scale; const char offset; const char negative; const char positive; const char decimal; const char leading; const char exact; } constant_formats[] = { { "0", 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, { "1", 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, { "4", 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, { "2", 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, { "uimm2", 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { "uimm3", 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { "imm3", 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, { "pcrel4", 4, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0}, { "imm4", 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, { "uimm4s4", 4, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0}, { "uimm4s4d", 4, 0, 0, 0, 2, 0, 0, 1, 1, 0, 0}, { "uimm4", 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { "uimm4s2", 4, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0}, { "negimm5s4", 5, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0}, { "imm5", 5, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, { "imm5d", 5, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0}, { "uimm5", 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { "imm6", 6, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, { "imm7", 7, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, { "imm7d", 7, 0, 1, 0, 0, 0, 0, 0, 1, 3, 0}, { "imm8", 8, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, { "uimm8", 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { "pcrel8", 8, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0}, { "uimm8s4", 8, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, { "pcrel8s4", 8, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0}, { "lppcrel10", 10, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0}, { "pcrel10", 10, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, { "pcrel12", 12, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, { "imm16s4", 16, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0}, { "luimm16", 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { "imm16", 16, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, { "imm16d", 16, 0, 1, 0, 0, 0, 0, 0, 1, 3, 0}, { "huimm16", 16, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { "rimm16", 16, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, { "imm16s2", 16, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0}, { "uimm16s4", 16, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0}, { "uimm16s4d", 16, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0}, { "uimm16", 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { "pcrel24", 24, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, { "uimm32", 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { "imm32", 32, 0, 1, 0, 0, 0, 0, 0, 1, 3, 0}, { "huimm32", 32, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, { "huimm32e", 32, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1}, }; static const char * fmtconst_str (const_forms_t cf, bs32 x, bu32 pc) { static char buf[60]; if (constant_formats[cf].reloc) { bu32 ea = (((constant_formats[cf].pcrel ? SIGNEXTEND (x, constant_formats[cf].nbits) : x) + constant_formats[cf].offset) << constant_formats[cf].scale); if (constant_formats[cf].pcrel) ea += pc; /*if (outf->symbol_at_address_func (ea, outf) || !constant_formats[cf].exact) { outf->print_address_func (ea, outf); return ""; } else*/ { sprintf (buf, "%#x", x); return buf; } } /* Negative constants have an implied sign bit. */ if (constant_formats[cf].negative) { int nb = constant_formats[cf].nbits + 1; x = x | (1 << constant_formats[cf].nbits); x = SIGNEXTEND (x, nb); } else x = constant_formats[cf].issigned ? SIGNEXTEND (x, constant_formats[cf].nbits) : x; if (constant_formats[cf].offset) x += constant_formats[cf].offset; if (constant_formats[cf].scale) x <<= constant_formats[cf].scale; if (constant_formats[cf].decimal) { if (constant_formats[cf].leading) { char ps[10]; sprintf (ps, "%%%ii", constant_formats[cf].leading); sprintf (buf, ps, x); } else sprintf (buf, "%i", x); } else { if (constant_formats[cf].issigned && x < 0) sprintf (buf, "-0x%x", abs (x)); else sprintf (buf, "0x%x", x); } return buf; } static bu32 fmtconst_val (const_forms_t cf, bu32 x, bu32 pc) { if (0 && constant_formats[cf].reloc) { bu32 ea = (((constant_formats[cf].pcrel ? (bu32)SIGNEXTEND (x, constant_formats[cf].nbits) : x) + constant_formats[cf].offset) << constant_formats[cf].scale); if (constant_formats[cf].pcrel) ea += pc; return ea; } /* Negative constants have an implied sign bit. */ if (constant_formats[cf].negative) { int nb = constant_formats[cf].nbits + 1; x = x | (1 << constant_formats[cf].nbits); x = SIGNEXTEND (x, nb); } else if (constant_formats[cf].issigned) x = SIGNEXTEND (x, constant_formats[cf].nbits); x += constant_formats[cf].offset; x <<= constant_formats[cf].scale; return x; } #define uimm16s4(x) fmtconst_val (c_uimm16s4, x, 0) #define uimm16s4_str(x) fmtconst_str (c_uimm16s4, x, 0) #define uimm16s4d(x) fmtconst_val (c_uimm16s4d, x, 0) #define pcrel4(x) fmtconst_val (c_pcrel4, x, pc) #define pcrel8(x) fmtconst_val (c_pcrel8, x, pc) #define pcrel8s4(x) fmtconst_val (c_pcrel8s4, x, pc) #define pcrel10(x) fmtconst_val (c_pcrel10, x, pc) #define pcrel12(x) fmtconst_val (c_pcrel12, x, pc) #define negimm5s4(x) fmtconst_val (c_negimm5s4, x, 0) #define negimm5s4_str(x) fmtconst_str (c_negimm5s4, x, 0) #define rimm16(x) fmtconst_val (c_rimm16, x, 0) #define huimm16(x) fmtconst_val (c_huimm16, x, 0) #define imm16(x) fmtconst_val (c_imm16, x, 0) #define imm16_str(x) fmtconst_str (c_imm16, x, 0) #define imm16d(x) fmtconst_val (c_imm16d, x, 0) #define uimm2(x) fmtconst_val (c_uimm2, x, 0) #define uimm3(x) fmtconst_val (c_uimm3, x, 0) #define uimm3_str(x) fmtconst_str (c_uimm3, x, 0) #define luimm16(x) fmtconst_val (c_luimm16, x, 0) #define luimm16_str(x) fmtconst_str (c_luimm16, x, 0) #define uimm4(x) fmtconst_val (c_uimm4, x, 0) #define uimm4_str(x) fmtconst_str (c_uimm4, x, 0) #define uimm5(x) fmtconst_val (c_uimm5, x, 0) #define uimm5_str(x) fmtconst_str (c_uimm5, x, 0) #define imm16s2(x) fmtconst_val (c_imm16s2, x, 0) #define imm16s2_str(x) fmtconst_str (c_imm16s2, x, 0) #define uimm8(x) fmtconst_val (c_uimm8, x, 0) #define imm16s4(x) fmtconst_val (c_imm16s4, x, 0) #define imm16s4_str(x) fmtconst_str (c_imm16s4, x, 0) #define uimm4s2(x) fmtconst_val (c_uimm4s2, x, 0) #define uimm4s2_str(x) fmtconst_str (c_uimm4s2, x, 0) #define uimm4s4(x) fmtconst_val (c_uimm4s4, x, 0) #define uimm4s4_str(x) fmtconst_str (c_uimm4s4, x, 0) #define uimm4s4d(x) fmtconst_val (c_uimm4s4d, x, 0) #define lppcrel10(x) fmtconst_val (c_lppcrel10, x, pc) #define imm3(x) fmtconst_val (c_imm3, x, 0) #define imm3_str(x) fmtconst_str (c_imm3, x, 0) #define imm4(x) fmtconst_val (c_imm4, x, 0) #define uimm8s4(x) fmtconst_val (c_uimm8s4, x, 0) #define imm5(x) fmtconst_val (c_imm5, x, 0) #define imm5d(x) fmtconst_val (c_imm5d, x, 0) #define imm6(x) fmtconst_val (c_imm6, x, 0) #define imm7(x) fmtconst_val (c_imm7, x, 0) #define imm7_str(x) fmtconst_str (c_imm7, x, 0) #define imm7d(x) fmtconst_val (c_imm7d, x, 0) #define imm8(x) fmtconst_val (c_imm8, x, 0) #define pcrel24(x) fmtconst_val (c_pcrel24, x, pc) #define pcrel24_str(x) fmtconst_str (c_pcrel24, x, pc) #define uimm16(x) fmtconst_val (c_uimm16, x, 0) #define uimm32(x) fmtconst_val (c_uimm32, x, 0) #define imm32(x) fmtconst_val (c_imm32, x, 0) #define huimm32(x) fmtconst_val (c_huimm32, x, 0) #define huimm32e(x) fmtconst_val (c_huimm32e, x, 0) /* Table C-4. Core Register Encoding Map. */ const char * const greg_names[] = { "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "P0", "P1", "P2", "P3", "P4", "P5", "SP", "FP", "I0", "I1", "I2", "I3", "M0", "M1", "M2", "M3", "B0", "B1", "B2", "B3", "L0", "L1", "L2", "L3", "A0.X", "A0.W", "A1.X", "A1.W", "", "", "ASTAT", "RETS", "", "", "", "", "", "", "", "", "LC0", "LT0", "LB0", "LC1", "LT1", "LB1", "CYCLES", "CYCLES2", "USP", "SEQSTAT", "SYSCFG", "RETI", "RETX", "RETN", "RETE", "EMUDAT", }; static const char * get_allreg_name (int grp, int reg) { return greg_names[(grp << 3) | reg]; } static const char * get_preg_name (int reg) { return get_allreg_name (1, reg); } static bool reg_is_reserved (int grp, int reg) { return (grp == 4 && (reg == 4 || reg == 5)) || (grp == 5); } static bu32 * get_allreg (SIM_CPU *cpu, int grp, int reg) { int fullreg = (grp << 3) | reg; /* REG_R0, REG_R1, REG_R2, REG_R3, REG_R4, REG_R5, REG_R6, REG_R7, REG_P0, REG_P1, REG_P2, REG_P3, REG_P4, REG_P5, REG_SP, REG_FP, REG_I0, REG_I1, REG_I2, REG_I3, REG_M0, REG_M1, REG_M2, REG_M3, REG_B0, REG_B1, REG_B2, REG_B3, REG_L0, REG_L1, REG_L2, REG_L3, REG_A0x, REG_A0w, REG_A1x, REG_A1w, , , REG_ASTAT, REG_RETS, , , , , , , , , REG_LC0, REG_LT0, REG_LB0, REG_LC1, REG_LT1, REG_LB1, REG_CYCLES, REG_CYCLES2, REG_USP, REG_SEQSTAT, REG_SYSCFG, REG_RETI, REG_RETX, REG_RETN, REG_RETE, REG_LASTREG */ switch (fullreg >> 2) { case 0: case 1: return &DREG (reg); case 2: case 3: return &PREG (reg); case 4: return &IREG (reg & 3); case 5: return &MREG (reg & 3); case 6: return &BREG (reg & 3); case 7: return &LREG (reg & 3); default: switch (fullreg) { case 32: return &AXREG (0); case 33: return &AWREG (0); case 34: return &AXREG (1); case 35: return &AWREG (1); case 39: return &RETSREG; case 48: return &LCREG (0); case 49: return <REG (0); case 50: return &LBREG (0); case 51: return &LCREG (1); case 52: return <REG (1); case 53: return &LBREG (1); case 54: return &CYCLESREG; case 55: return &CYCLES2REG; case 56: return &USPREG; case 57: return &SEQSTATREG; case 58: return &SYSCFGREG; case 59: return &RETIREG; case 60: return &RETXREG; case 61: return &RETNREG; case 62: return &RETEREG; case 63: return &EMUDAT_INREG; } illegal_instruction (cpu); } } static const char * amod0 (int s0, int x0) { static const char * const mod0[] = { "", " (S)", " (CO)", " (SCO)", }; int i = s0 + (x0 << 1); if (i < ARRAY_SIZE (mod0)) return mod0[i]; else return ""; } static const char * amod0amod2 (int s0, int x0, int aop0) { static const char * const mod02[] = { "", " (S)", " (CO)", " (SCO)", "", "", "", "", " (ASR)", " (S, ASR)", " (CO, ASR)", " (SCO, ASR)", " (ASL)", " (S, ASL)", " (CO, ASL)", " (SCO, ASL)", }; int i = s0 + (x0 << 1) + (aop0 << 2); if (i < ARRAY_SIZE (mod02)) return mod02[i]; else return ""; } static const char * amod1 (int s0, int x0) { static const char * const mod1[] = { " (NS)", " (S)", }; int i = s0 + (x0 << 1); if (i < ARRAY_SIZE (mod1)) return mod1[i]; else return ""; } static const char * mac_optmode (int mmod, int MM) { static const char * const omode[] = { [(M_S2RND << 1) + 0] = " (S2RND)", [(M_T << 1) + 0] = " (T)", [(M_W32 << 1) + 0] = " (W32)", [(M_FU << 1) + 0] = " (FU)", [(M_TFU << 1) + 0] = " (TFU)", [(M_IS << 1) + 0] = " (IS)", [(M_ISS2 << 1) + 0] = " (ISS2)", [(M_IH << 1) + 0] = " (IH)", [(M_IU << 1) + 0] = " (IU)", [(M_S2RND << 1) + 1] = " (M, S2RND)", [(M_T << 1) + 1] = " (M, T)", [(M_W32 << 1) + 1] = " (M, W32)", [(M_FU << 1) + 1] = " (M, FU)", [(M_TFU << 1) + 1] = " (M, TFU)", [(M_IS << 1) + 1] = " (M, IS)", [(M_ISS2 << 1) + 1] = " (M, ISS2)", [(M_IH << 1) + 1] = " (M, IH)", [(M_IU << 1) + 1] = " (M, IU)", }; int i = MM + (mmod << 1); if (i < ARRAY_SIZE (omode) && omode[i]) return omode[i]; else return ""; } static const char * get_store_name (SIM_CPU *cpu, bu32 *p) { if (p >= &DREG (0) && p <= &CYCLESREG) return greg_names[p - &DREG (0)]; else if (p == &AXREG (0)) return greg_names[4 * 8 + 0]; else if (p == &AWREG (0)) return greg_names[4 * 8 + 1]; else if (p == &AXREG (1)) return greg_names[4 * 8 + 2]; else if (p == &AWREG (1)) return greg_names[4 * 8 + 3]; else if (p == &ASTATREG (av0)) return "ASTAT[av0]"; else if (p == &ASTATREG (av0s)) return "ASTAT[av0s]"; else if (p == &ASTATREG (av1)) return "ASTAT[av1]"; else if (p == &ASTATREG (av1s)) return "ASTAT[av1s]"; else if (p == &ASTATREG (v)) return "ASTAT[v]"; else if (p == &ASTATREG (vs)) return "ASTAT[vs]"; else if (p == &ASTATREG (v_copy)) return "ASTAT[v_copy]"; else if (p == &ASTATREG (az)) return "ASTAT[az]"; else if (p == &ASTATREG (an)) return "ASTAT[an]"; else if (p == &ASTATREG (az)) return "ASTAT[az]"; else if (p == &ASTATREG (ac0)) return "ASTAT[ac0]"; else if (p == &ASTATREG (ac0_copy)) return "ASTAT[ac0_copy]"; else { /* Worry about this when we start to STORE() it. */ sim_io_eprintf (CPU_STATE (cpu), "STORE(): unknown register\n"); abort (); } } static void queue_store (SIM_CPU *cpu, bu32 *addr, bu32 val) { struct store *s = &BFIN_CPU_STATE.stores[BFIN_CPU_STATE.n_stores]; s->addr = addr; s->val = val; TRACE_REGISTER (cpu, "queuing write %s = %#x", get_store_name (cpu, addr), val); ++BFIN_CPU_STATE.n_stores; } #define STORE(X, Y) \ do { \ if (BFIN_CPU_STATE.n_stores == 20) abort (); \ queue_store (cpu, &(X), (Y)); \ } while (0) static void setflags_nz (SIM_CPU *cpu, bu32 val) { SET_ASTATREG (az, val == 0); SET_ASTATREG (an, val >> 31); } static void setflags_nz_2x16 (SIM_CPU *cpu, bu32 val) { SET_ASTATREG (an, (bs16)val < 0 || (bs16)(val >> 16) < 0); SET_ASTATREG (az, (bs16)val == 0 || (bs16)(val >> 16) == 0); } static void setflags_logical (SIM_CPU *cpu, bu32 val) { setflags_nz (cpu, val); SET_ASTATREG (ac0, 0); SET_ASTATREG (v, 0); } static bu32 add_brev (bu32 addend1, bu32 addend2) { bu32 mask, b, r; int i, cy; mask = 0x80000000; r = 0; cy = 0; for (i = 31; i >= 0; --i) { b = ((addend1 & mask) >> i) + ((addend2 & mask) >> i); b += cy; cy = b >> 1; b &= 1; r |= b << i; mask >>= 1; } return r; } /* This is a bit crazy, but we want to simulate the hardware behavior exactly rather than worry about the circular buffers being used correctly. Which isn't to say there isn't room for improvement here, just that we want to be conservative. See also dagsub(). */ static bu32 dagadd (SIM_CPU *cpu, int dagno, bs32 M) { bu64 i = IREG (dagno); bu64 l = LREG (dagno); bu64 b = BREG (dagno); bu64 m = (bu32)M; bu64 LB, IM, IML; bu32 im32, iml32, lb32, res; bu64 msb, car; /* A naïve implementation that mostly works: res = i + m; if (l && res >= b + l) res -= l; STORE (IREG (dagno), res); */ msb = (bu64)1 << 31; car = (bu64)1 << 32; IM = i + m; im32 = IM; LB = l + b; lb32 = LB; if (M < 0) { IML = i + m + l; iml32 = IML; if ((i & msb) || (IM & car)) res = (im32 < b) ? iml32 : im32; else res = (im32 < b) ? im32 : iml32; } else { IML = i + m - l; iml32 = IML; if ((IM & car) == (LB & car)) res = (im32 < lb32) ? im32 : iml32; else res = (im32 < lb32) ? iml32 : im32; } STORE (IREG (dagno), res); return res; } /* See dagadd() notes above. */ static bu32 dagsub (SIM_CPU *cpu, int dagno, bs32 M) { bu64 i = IREG (dagno); bu64 l = LREG (dagno); bu64 b = BREG (dagno); bu64 m = (bu32)M; bu64 mbar = (bu32)(~m + 1); bu64 LB, IM, IML; bu32 b32, im32, iml32, lb32, res; bu64 msb, car; /* A naïve implementation that mostly works: res = i - m; if (l && newi < b) newi += l; STORE (IREG (dagno), newi); */ msb = (bu64)1 << 31; car = (bu64)1 << 32; IM = i + mbar; im32 = IM; LB = l + b; lb32 = LB; if (M < 0) { IML = i + mbar - l; iml32 = IML; if (!!((i & msb) && (IM & car)) == !!(LB & car)) res = (im32 < lb32) ? im32 : iml32; else res = (im32 < lb32) ? iml32 : im32; } else { IML = i + mbar + l; iml32 = IML; b32 = b; if (M == 0 || IM & car) res = (im32 < b32) ? iml32 : im32; else res = (im32 < b32) ? im32 : iml32; } STORE (IREG (dagno), res); return res; } static bu40 ashiftrt (SIM_CPU *cpu, bu40 val, int cnt, int size) { int real_cnt = cnt > size ? size : cnt; bu40 sgn = ~(((val & 0xFFFFFFFFFFull) >> (size - 1)) - 1); int sgncnt = size - real_cnt; if (sgncnt > 16) sgn <<= 16, sgncnt -= 16; sgn <<= sgncnt; if (real_cnt > 16) val >>= 16, real_cnt -= 16; val >>= real_cnt; val |= sgn; SET_ASTATREG (an, val >> (size - 1)); SET_ASTATREG (az, val == 0); /* XXX: Need to check ASTAT[v] behavior here. */ SET_ASTATREG (v, 0); return val; } static bu64 lshiftrt (SIM_CPU *cpu, bu64 val, int cnt, int size) { int real_cnt = cnt > size ? size : cnt; if (real_cnt > 16) val >>= 16, real_cnt -= 16; val >>= real_cnt; switch (size) { case 16: val &= 0xFFFF; break; case 32: val &= 0xFFFFFFFF; break; case 40: val &= 0xFFFFFFFFFFull; break; default: illegal_instruction (cpu); break; } SET_ASTATREG (an, val >> (size - 1)); SET_ASTATREG (az, val == 0); SET_ASTATREG (v, 0); return val; } static bu64 lshift (SIM_CPU *cpu, bu64 val, int cnt, int size, bool saturate) { int i, j, real_cnt = cnt > size ? size : cnt; bu64 sgn = ~((val >> (size - 1)) - 1); int mask_cnt = size - 1; bu64 masked, new_val = val, tmp; bu64 mask = ~0; mask <<= mask_cnt; sgn <<= mask_cnt; masked = val & mask; if (real_cnt > 16) new_val <<= 16, real_cnt -= 16; new_val <<= real_cnt; masked = new_val & mask; /* If an operation would otherwise cause a positive value to overflow and become negative, instead, saturation limits the result to the maximum positive value for the size register being used. Conversely, if an operation would otherwise cause a negative value to overflow and become positive, saturation limits the result to the maximum negative value for the register size. However, it's a little more complex than looking at sign bits, we need to see if we are shifting the sign information away... */ tmp = val & ((~mask << 1) | 1); j = 0; for (i = 1; i <= real_cnt && saturate; i++) { if ((tmp & ((bu64)1 << (size - 1))) != (((val >> mask_cnt) & 0x1) << mask_cnt)) j++; tmp <<= 1; } saturate &= (!sgn && (new_val & (1 << mask_cnt))) || (sgn && !(new_val & (1 << mask_cnt))); switch (size) { case 16: if (j || (saturate && (new_val & mask))) new_val = sgn == 0 ? 0x7fff : 0x8000, saturate = 1; new_val &= 0xFFFF; break; case 32: new_val &= 0xFFFFFFFF; masked &= 0xFFFFFFFF; if (j || (saturate && ((sgn != masked) || (!sgn && new_val == 0)))) new_val = sgn == 0 ? 0x7fffffff : 0x80000000, saturate = 1; break; case 40: new_val &= 0xFFFFFFFFFFull; masked &= 0xFFFFFFFFFFull; break; default: illegal_instruction (cpu); break; } SET_ASTATREG (an, new_val >> (size - 1)); SET_ASTATREG (az, new_val == 0); SET_ASTATREG (v, !!(saturate || j)); if (saturate || j) SET_ASTATREG (vs, 1); return new_val; } static bu32 algn (bu32 l, bu32 h, bu32 aln) { if (aln == 0) return l; else return (l >> (8 * aln)) | (h << (32 - 8 * aln)); } static bu32 saturate_s16 (bu64 val, bu32 *overflow) { if ((bs64)val < -0x8000ll) { if (overflow) *overflow = 1; return 0x8000; } if ((bs64)val > 0x7fff) { if (overflow) *overflow = 1; return 0x7fff; } return val & 0xffff; } static bu40 rot40 (bu40 val, int shift, bu32 *cc) { const int nbits = 40; bu40 ret; shift = CLAMP (shift, -nbits, nbits); if (shift == 0) return val; /* Reduce everything to rotate left. */ if (shift < 0) shift += nbits + 1; ret = shift == nbits ? 0 : val << shift; ret |= shift == 1 ? 0 : val >> ((nbits + 1) - shift); ret |= (bu40)*cc << (shift - 1); *cc = (val >> (nbits - shift)) & 1; return ret; } static bu32 rot32 (bu32 val, int shift, bu32 *cc) { const int nbits = 32; bu32 ret; shift = CLAMP (shift, -nbits, nbits); if (shift == 0) return val; /* Reduce everything to rotate left. */ if (shift < 0) shift += nbits + 1; ret = shift == nbits ? 0 : val << shift; ret |= shift == 1 ? 0 : val >> ((nbits + 1) - shift); ret |= (bu32)*cc << (shift - 1); *cc = (val >> (nbits - shift)) & 1; return ret; } static bu32 add32 (SIM_CPU *cpu, bu32 a, bu32 b, int carry, int sat) { int flgs = (a >> 31) & 1; int flgo = (b >> 31) & 1; bu32 v = a + b; int flgn = (v >> 31) & 1; int overflow = (flgs ^ flgn) & (flgo ^ flgn); if (sat && overflow) { v = (bu32)1 << 31; if (flgn) v -= 1; flgn = (v >> 31) & 1; } SET_ASTATREG (an, flgn); if (overflow) SET_ASTATREG (vs, 1); SET_ASTATREG (v, overflow); ASTATREG (v_internal) |= overflow; SET_ASTATREG (az, v == 0); if (carry) SET_ASTATREG (ac0, ~a < b); return v; } static bu32 sub32 (SIM_CPU *cpu, bu32 a, bu32 b, int carry, int sat, int parallel) { int flgs = (a >> 31) & 1; int flgo = (b >> 31) & 1; bu32 v = a - b; int flgn = (v >> 31) & 1; int overflow = (flgs ^ flgo) & (flgn ^ flgs); if (sat && overflow) { v = (bu32)1 << 31; if (flgn) v -= 1; flgn = (v >> 31) & 1; } if (!parallel || flgn) SET_ASTATREG (an, flgn); if (overflow) SET_ASTATREG (vs, 1); if (!parallel || overflow) SET_ASTATREG (v, overflow); if (!parallel || overflow) ASTATREG (v_internal) |= overflow; if (!parallel || v == 0) SET_ASTATREG (az, v == 0); if (carry && (!parallel || b <= a)) SET_ASTATREG (ac0, b <= a); return v; } static bu32 add16 (SIM_CPU *cpu, bu16 a, bu16 b, bu32 *carry, bu32 *overfl, bu32 *zero, bu32 *neg, int sat, int scale) { int flgs = (a >> 15) & 1; int flgo = (b >> 15) & 1; bs64 v = (bs16)a + (bs16)b; int flgn = (v >> 15) & 1; int overflow = (flgs ^ flgn) & (flgo ^ flgn); switch (scale) { case 0: break; case 2: /* (ASR) */ v = (a >> 1) + (a & 0x8000) + (b >> 1) + (b & 0x8000) + (((a & 1) + (b & 1)) >> 1); v |= -(v & 0x8000); break; case 3: /* (ASL) */ v = (v << 1); break; default: illegal_instruction (cpu); } flgn = (v >> 15) & 1; overflow = (flgs ^ flgn) & (flgo ^ flgn); if (v > (bs64)0xffff) overflow = 1; if (sat) v = saturate_s16 (v, 0); if (neg) *neg |= (v >> 15) & 1; if (overfl) *overfl |= overflow; if (zero) *zero |= (v & 0xFFFF) == 0; if (carry) *carry |= ((bu16)~a < (bu16)b); return v & 0xffff; } static bu32 sub16 (SIM_CPU *cpu, bu16 a, bu16 b, bu32 *carry, bu32 *overfl, bu32 *zero, bu32 *neg, int sat, int scale) { int flgs = (a >> 15) & 1; int flgo = (b >> 15) & 1; bs64 v = (bs16)a - (bs16)b; int flgn = (v >> 15) & 1; int overflow = (flgs ^ flgo) & (flgn ^ flgs); switch (scale) { case 0: break; case 2: /* (ASR) */ if (sat) v = ((a >> 1) + (a & 0x8000)) - ( (b >> 1) + (b & 0x8000)) + (((a & 1)-(b & 1))); else { v = ((v & 0xFFFF) >> 1); if ((!flgs & !flgo & flgn) || (flgs & !flgo & !flgn) || (flgs & flgo & flgn) || (flgs & !flgo & flgn)) v |= 0x8000; } v |= -(v & 0x8000); flgn = (v >> 15) & 1; overflow = (flgs ^ flgo) & (flgn ^ flgs); break; case 3: /* (ASL) */ v <<= 1; if (v > (bs64)0x7fff || v < (bs64)-0xffff) overflow = 1; break; default: illegal_instruction (cpu); } if (sat) { v = saturate_s16 (v, 0); } if (neg) *neg |= (v >> 15) & 1; if (zero) *zero |= (v & 0xFFFF) == 0; if (overfl) *overfl |= overflow; if (carry) *carry |= (bu16)b <= (bu16)a; return v; } static bu32 min32 (SIM_CPU *cpu, bu32 a, bu32 b) { int val = a; if ((bs32)a > (bs32)b) val = b; setflags_nz (cpu, val); SET_ASTATREG (v, 0); return val; } static bu32 max32 (SIM_CPU *cpu, bu32 a, bu32 b) { int val = a; if ((bs32)a < (bs32)b) val = b; setflags_nz (cpu, val); SET_ASTATREG (v, 0); return val; } static bu32 min2x16 (SIM_CPU *cpu, bu32 a, bu32 b) { int val = a; if ((bs16)a > (bs16)b) val = (val & 0xFFFF0000) | (b & 0xFFFF); if ((bs16)(a >> 16) > (bs16)(b >> 16)) val = (val & 0xFFFF) | (b & 0xFFFF0000); setflags_nz_2x16 (cpu, val); SET_ASTATREG (v, 0); return val; } static bu32 max2x16 (SIM_CPU *cpu, bu32 a, bu32 b) { int val = a; if ((bs16)a < (bs16)b) val = (val & 0xFFFF0000) | (b & 0xFFFF); if ((bs16)(a >> 16) < (bs16)(b >> 16)) val = (val & 0xFFFF) | (b & 0xFFFF0000); setflags_nz_2x16 (cpu, val); SET_ASTATREG (v, 0); return val; } static bu32 add_and_shift (SIM_CPU *cpu, bu32 a, bu32 b, int shift) { int v; ASTATREG (v_internal) = 0; v = add32 (cpu, a, b, 0, 0); while (shift-- > 0) { int x = (v >> 30) & 0x3; if (x == 1 || x == 2) ASTATREG (v_internal) = 1; v <<= 1; } SET_ASTATREG (az, v == 0); SET_ASTATREG (an, v & 0x80000000); SET_ASTATREG (v, ASTATREG (v_internal)); if (ASTATREG (v)) SET_ASTATREG (vs, 1); return v; } static bu32 xor_reduce (bu64 acc0, bu64 acc1) { int i; bu32 v = 0; for (i = 0; i < 40; ++i) { v ^= (acc0 & acc1 & 1); acc0 >>= 1; acc1 >>= 1; } return v; } /* DIVS ( Dreg, Dreg ) ; Initialize for DIVQ. Set the AQ status bit based on the signs of the 32-bit dividend and the 16-bit divisor. Left shift the dividend one bit. Copy AQ into the dividend LSB. */ static bu32 divs (SIM_CPU *cpu, bu32 pquo, bu16 divisor) { bu16 r = pquo >> 16; int aq; aq = (r ^ divisor) >> 15; /* Extract msb's and compute quotient bit. */ SET_ASTATREG (aq, aq); /* Update global quotient state. */ pquo <<= 1; pquo |= aq; pquo = (pquo & 0x1FFFF) | (r << 17); return pquo; } /* DIVQ ( Dreg, Dreg ) ; Based on AQ status bit, either add or subtract the divisor from the dividend. Then set the AQ status bit based on the MSBs of the 32-bit dividend and the 16-bit divisor. Left shift the dividend one bit. Copy the logical inverse of AQ into the dividend LSB. */ static bu32 divq (SIM_CPU *cpu, bu32 pquo, bu16 divisor) { unsigned short af = pquo >> 16; unsigned short r; int aq; if (ASTATREG (aq)) r = divisor + af; else r = af - divisor; aq = (r ^ divisor) >> 15; /* Extract msb's and compute quotient bit. */ SET_ASTATREG (aq, aq); /* Update global quotient state. */ pquo <<= 1; pquo |= !aq; pquo = (pquo & 0x1FFFF) | (r << 17); return pquo; } /* ONES ( Dreg ) ; Count the number of bits set to 1 in the 32bit value. */ static bu32 ones (bu32 val) { bu32 i; bu32 ret; ret = 0; for (i = 0; i < 32; ++i) ret += !!(val & (1 << i)); return ret; } static void reg_check_sup (SIM_CPU *cpu, int grp, int reg) { if (grp == 7) cec_require_supervisor (cpu); } static void reg_write (SIM_CPU *cpu, int grp, int reg, bu32 value) { bu32 *whichreg; /* ASTAT is special! */ if (grp == 4 && reg == 6) { SET_ASTAT (value); return; } /* Check supervisor after get_allreg() so exception order is correct. */ whichreg = get_allreg (cpu, grp, reg); reg_check_sup (cpu, grp, reg); if (whichreg == &CYCLES2REG) /* Writes to CYCLES2 goes to the shadow. */ whichreg = &CYCLES2SHDREG; else if (whichreg == &SEQSTATREG) /* Register is read only -- discard writes. */ return; else if (whichreg == &EMUDAT_INREG) /* Writes to EMUDAT goes to the output. */ whichreg = &EMUDAT_OUTREG; else if (whichreg == <REG (0) || whichreg == <REG (1)) /* Writes to LT clears LSB automatically. */ value &= ~0x1; else if (whichreg == &AXREG (0) || whichreg == &AXREG (1)) value &= 0xFF; TRACE_REGISTER (cpu, "wrote %s = %#x", get_allreg_name (grp, reg), value); *whichreg = value; } static bu32 reg_read (SIM_CPU *cpu, int grp, int reg) { bu32 *whichreg; bu32 value; /* ASTAT is special! */ if (grp == 4 && reg == 6) return ASTAT; /* Check supervisor after get_allreg() so exception order is correct. */ whichreg = get_allreg (cpu, grp, reg); reg_check_sup (cpu, grp, reg); value = *whichreg; if (whichreg == &CYCLESREG) /* Reads of CYCLES reloads CYCLES2 from the shadow. */ SET_CYCLES2REG (CYCLES2SHDREG); else if ((whichreg == &AXREG (1) || whichreg == &AXREG (0)) && (value & 0x80)) /* Sign extend if necessary. */ value |= 0xFFFFFF00; return value; } static bu64 get_extended_cycles (SIM_CPU *cpu) { return ((bu64)CYCLES2SHDREG << 32) | CYCLESREG; } /* We can't re-use sim_events_time() because the CYCLES registers may be written/cleared/reset/stopped/started at any time by software. */ static void cycles_inc (SIM_CPU *cpu, bu32 inc) { bu64 cycles; bu32 cycles2; if (!(SYSCFGREG & SYSCFG_CCEN)) return; cycles = get_extended_cycles (cpu) + inc; SET_CYCLESREG (cycles); cycles2 = cycles >> 32; if (CYCLES2SHDREG != cycles2) SET_CYCLES2SHDREG (cycles2); } static bu64 get_unextended_acc (SIM_CPU *cpu, int which) { return ((bu64)(AXREG (which) & 0xff) << 32) | AWREG (which); } static bu64 get_extended_acc (SIM_CPU *cpu, int which) { bu64 acc = AXREG (which); /* Sign extend accumulator values before adding. */ if (acc & 0x80) acc |= -0x80; else acc &= 0xFF; acc <<= 32; acc |= AWREG (which); return acc; } /* Perform a multiplication of D registers SRC0 and SRC1, sign- or zero-extending the result to 64 bit. H0 and H1 determine whether the high part or the low part of the source registers is used. Store 1 in *PSAT if saturation occurs, 0 otherwise. */ static bu64 decode_multfunc (SIM_CPU *cpu, int h0, int h1, int src0, int src1, int mmod, int MM, bu32 *psat) { bu32 s0 = DREG (src0), s1 = DREG (src1); bu32 sgn0, sgn1; bu32 val; bu64 val1; if (h0) s0 >>= 16; if (h1) s1 >>= 16; s0 &= 0xffff; s1 &= 0xffff; sgn0 = -(s0 & 0x8000); sgn1 = -(s1 & 0x8000); if (MM) s0 |= sgn0; else switch (mmod) { case 0: case M_S2RND: case M_T: case M_IS: case M_ISS2: case M_IH: case M_W32: s0 |= sgn0; s1 |= sgn1; break; case M_FU: case M_IU: case M_TFU: break; default: illegal_instruction (cpu); } val = s0 * s1; /* Perform shift correction if appropriate for the mode. */ *psat = 0; if (!MM && (mmod == 0 || mmod == M_T || mmod == M_S2RND || mmod == M_W32)) { if (val == 0x40000000) { if (mmod == M_W32) val = 0x7fffffff; else val = 0x80000000; *psat = 1; } else val <<= 1; } val1 = val; if (mmod == 0 || mmod == M_IS || mmod == M_T || mmod == M_S2RND || mmod == M_ISS2 || mmod == M_IH || (MM && mmod == M_FU)) val1 |= -(val1 & 0x80000000); if (*psat) val1 &= 0xFFFFFFFFull; return val1; } static bu40 saturate_s40_astat (bu64 val, bu32 *v) { if ((bs64)val < -((bs64)1 << 39)) { *v = 1; return -((bs64)1 << 39); } else if ((bs64)val >= ((bs64)1 << 39) - 1) { *v = 1; return ((bu64)1 << 39) - 1; } *v = 0; /* No overflow. */ return val; } static bu40 saturate_s40 (bu64 val) { bu32 v; return saturate_s40_astat (val, &v); } static bu32 saturate_s32 (bu64 val, bu32 *overflow) { if ((bs64)val < -0x80000000ll) { if (overflow) *overflow = 1; return 0x80000000; } if ((bs64)val > 0x7fffffff) { if (overflow) *overflow = 1; return 0x7fffffff; } return val; } static bu32 saturate_u32 (bu64 val, bu32 *overflow) { if (val > 0xffffffff) { if (overflow) *overflow = 1; return 0xffffffff; } return val; } static bu32 saturate_u16 (bu64 val, bu32 *overflow) { if (val > 0xffff) { if (overflow) *overflow = 1; return 0xffff; } return val; } static bu64 rnd16 (bu64 val) { bu64 sgnbits; /* FIXME: Should honour rounding mode. */ if ((val & 0xffff) > 0x8000 || ((val & 0xffff) == 0x8000 && (val & 0x10000))) val += 0x8000; sgnbits = val & 0xffff000000000000ull; val >>= 16; return val | sgnbits; } static bu64 trunc16 (bu64 val) { bu64 sgnbits = val & 0xffff000000000000ull; val >>= 16; return val | sgnbits; } static int signbits (bu64 val, int size) { bu64 mask = (bu64)1 << (size - 1); bu64 bit = val & mask; int count = 0; for (;;) { mask >>= 1; bit >>= 1; if (mask == 0) break; if ((val & mask) != bit) break; count++; } if (size == 40) count -= 8; return count; } /* Extract a 16 or 32 bit value from a 64 bit multiplication result. These 64 bits must be sign- or zero-extended properly from the source we want to extract, either a 32 bit multiply or a 40 bit accumulator. */ static bu32 extract_mult (SIM_CPU *cpu, bu64 res, int mmod, int MM, int fullword, bu32 *overflow) { if (fullword) switch (mmod) { case 0: case M_IS: return saturate_s32 (res, overflow); case M_FU: if (MM) return saturate_s32 (res, overflow); return saturate_u32 (res, overflow); case M_S2RND: case M_ISS2: return saturate_s32 (res << 1, overflow); default: illegal_instruction (cpu); } else switch (mmod) { case 0: case M_W32: return saturate_s16 (rnd16 (res), overflow); case M_IH: return saturate_s32 (rnd16 (res), overflow) & 0xFFFF; case M_IS: return saturate_s16 (res, overflow); case M_FU: if (MM) return saturate_s16 (rnd16 (res), overflow); return saturate_u16 (rnd16 (res), overflow); case M_IU: if (MM) return saturate_s16 (res, overflow); return saturate_u16 (res, overflow); case M_T: return saturate_s16 (trunc16 (res), overflow); case M_TFU: return saturate_u16 (trunc16 (res), overflow); case M_S2RND: return saturate_s16 (rnd16 (res << 1), overflow); case M_ISS2: return saturate_s16 (res << 1, overflow); default: illegal_instruction (cpu); } } static bu32 decode_macfunc (SIM_CPU *cpu, int which, int op, int h0, int h1, int src0, int src1, int mmod, int MM, int fullword, bu32 *overflow) { bu64 acc; bu32 sat = 0, tsat; /* Sign extend accumulator if necessary, otherwise unsigned. */ if (mmod == 0 || mmod == M_T || mmod == M_IS || mmod == M_ISS2 || mmod == M_S2RND || mmod == M_IH || mmod == M_W32) acc = get_extended_acc (cpu, which); else acc = get_unextended_acc (cpu, which); if (MM && (mmod == M_T || mmod == M_IS || mmod == M_ISS2 || mmod == M_S2RND || mmod == M_IH || mmod == M_W32)) acc |= -(acc & 0x80000000); if (op != 3) { bu8 sgn0 = (acc >> 31) & 1; /* This can't saturate, so we don't keep track of the sat flag. */ bu64 res = decode_multfunc (cpu, h0, h1, src0, src1, mmod, MM, &tsat); /* Perform accumulation. */ switch (op) { case 0: acc = res; sgn0 = (acc >> 31) & 1; break; case 1: acc = acc + res; break; case 2: acc = acc - res; break; } /* Saturate. */ switch (mmod) { case 0: case M_T: case M_IS: case M_ISS2: case M_S2RND: if ((bs64)acc < -((bs64)1 << 39)) acc = -((bu64)1 << 39), sat = 1; else if ((bs64)acc > 0x7fffffffffll) acc = 0x7fffffffffull, sat = 1; break; case M_TFU: if (!MM && acc > 0xFFFFFFFFFFull) acc = 0x0, sat = 1; if (MM && acc > 0xFFFFFFFF) acc &= 0xFFFFFFFF; break; case M_IU: if (acc & 0x8000000000000000ull) acc = 0x0, sat = 1; if (acc > 0xFFFFFFFFFFull) acc &= 0xFFFFFFFFFFull, sat = 1; if (MM && acc > 0xFFFFFFFF) acc &= 0xFFFFFFFF; if (acc & 0x80000000) acc |= 0xffffffff00000000ull; break; case M_FU: if (!MM && (bs64)acc < 0) acc = 0x0, sat = 1; if (MM && (bs64)acc < -((bs64)1 << 39)) acc = -((bu64)1 << 39), sat = 1; if (!MM && (bs64)acc > (bs64)0xFFFFFFFFFFll) acc = 0xFFFFFFFFFFull, sat = 1; if (MM && acc > 0xFFFFFFFFFFull) acc &= 0xFFFFFFFFFFull; if (MM && acc & 0x80000000) acc |= 0xffffffff00000000ull; break; case M_IH: if ((bs64)acc < -0x80000000ll) acc = -0x80000000ull, sat = 1; else if ((bs64)acc >= 0x7fffffffll) acc = 0x7fffffffull, sat = 1; break; case M_W32: if (sgn0 && (sgn0 != ((acc >> 31) & 1)) && (((acc >> 32) & 0xFF) == 0xff)) acc = 0x80000000; acc &= 0xffffffff; if (acc & 0x80000000) acc |= 0xffffffff00000000ull; break; default: illegal_instruction (cpu); } } STORE (AXREG (which), (acc >> 32) & 0xff); STORE (AWREG (which), acc & 0xffffffff); STORE (ASTATREG (av[which]), sat); if (sat) STORE (ASTATREG (avs[which]), sat); return extract_mult (cpu, acc, mmod, MM, fullword, overflow); } bu32 hwloop_get_next_pc (SIM_CPU *cpu, bu32 pc, bu32 insn_len) { int i; if (insn_len == 0) return pc; /* If our PC has reached the bottom of a hardware loop, move back up to the top of the hardware loop. */ for (i = 1; i >= 0; --i) if (LCREG (i) > 1 && pc == LBREG (i)) { TRACE_BRANCH (cpu, pc, LTREG (i), i, "Hardware loop %i", i); return LTREG (i); } return pc + insn_len; } static void decode_ProgCtrl_0 (SIM_CPU *cpu, bu16 iw0, bu32 pc) { /* ProgCtrl +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |.prgfunc.......|.poprnd........| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int poprnd = ((iw0 >> ProgCtrl_poprnd_bits) & ProgCtrl_poprnd_mask); int prgfunc = ((iw0 >> ProgCtrl_prgfunc_bits) & ProgCtrl_prgfunc_mask); TRACE_EXTRACT (cpu, "%s: poprnd:%i prgfunc:%i", __func__, poprnd, prgfunc); if (prgfunc == 0 && poprnd == 0) { PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_nop); TRACE_INSN (cpu, "NOP;"); } else if (prgfunc == 1 && poprnd == 0) { bu32 newpc = RETSREG; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_branch); TRACE_INSN (cpu, "RTS;"); IFETCH_CHECK (newpc); if (INSN_LEN == 8) illegal_instruction_combination (cpu); TRACE_BRANCH (cpu, pc, newpc, -1, "RTS"); SET_PCREG (newpc); BFIN_CPU_STATE.did_jump = true; CYCLE_DELAY = 5; } else if (prgfunc == 1 && poprnd == 1) { PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_branch); TRACE_INSN (cpu, "RTI;"); /* Do not do IFETCH_CHECK here -- LSB has special meaning. */ if (INSN_LEN == 8) illegal_instruction_combination (cpu); cec_return (cpu, -1); CYCLE_DELAY = 5; } else if (prgfunc == 1 && poprnd == 2) { bu32 newpc = RETXREG; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_branch); TRACE_INSN (cpu, "RTX;"); /* XXX: Not sure if this is what the hardware does. */ IFETCH_CHECK (newpc); if (INSN_LEN == 8) illegal_instruction_combination (cpu); cec_return (cpu, IVG_EVX); CYCLE_DELAY = 5; } else if (prgfunc == 1 && poprnd == 3) { bu32 newpc = RETNREG; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_branch); TRACE_INSN (cpu, "RTN;"); /* XXX: Not sure if this is what the hardware does. */ IFETCH_CHECK (newpc); if (INSN_LEN == 8) illegal_instruction_combination (cpu); cec_return (cpu, IVG_NMI); CYCLE_DELAY = 5; } else if (prgfunc == 1 && poprnd == 4) { PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_branch); TRACE_INSN (cpu, "RTE;"); if (INSN_LEN == 8) illegal_instruction_combination (cpu); cec_return (cpu, IVG_EMU); CYCLE_DELAY = 5; } else if (prgfunc == 2 && poprnd == 0) { SIM_DESC sd = CPU_STATE (cpu); sim_events *events = STATE_EVENTS (sd); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_sync); /* XXX: in supervisor mode, utilizes wake up sources in user mode, it's a NOP ... */ TRACE_INSN (cpu, "IDLE;"); if (INSN_LEN == 8) illegal_instruction_combination (cpu); /* Timewarp ! */ if (events->queue) CYCLE_DELAY = events->time_from_event; else abort (); /* XXX: Should this ever happen ? */ } else if (prgfunc == 2 && poprnd == 3) { PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_sync); /* Just NOP it. */ TRACE_INSN (cpu, "CSYNC;"); if (INSN_LEN == 8) illegal_instruction_combination (cpu); CYCLE_DELAY = 10; } else if (prgfunc == 2 && poprnd == 4) { PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_sync); /* Just NOP it. */ TRACE_INSN (cpu, "SSYNC;"); if (INSN_LEN == 8) illegal_instruction_combination (cpu); /* Really 10+, but no model info for this. */ CYCLE_DELAY = 10; } else if (prgfunc == 2 && poprnd == 5) { PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_cec); TRACE_INSN (cpu, "EMUEXCPT;"); if (INSN_LEN == 8) illegal_instruction_combination (cpu); cec_exception (cpu, VEC_SIM_TRAP); } else if (prgfunc == 3 && poprnd < 8) { PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_cec); TRACE_INSN (cpu, "CLI R%i;", poprnd); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_DREG (poprnd, cec_cli (cpu)); } else if (prgfunc == 4 && poprnd < 8) { PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_cec); TRACE_INSN (cpu, "STI R%i;", poprnd); if (INSN_LEN == 8) illegal_instruction_combination (cpu); cec_sti (cpu, DREG (poprnd)); CYCLE_DELAY = 3; } else if (prgfunc == 5 && poprnd < 8) { bu32 newpc = PREG (poprnd); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_branch); TRACE_INSN (cpu, "JUMP (%s);", get_preg_name (poprnd)); IFETCH_CHECK (newpc); if (INSN_LEN == 8) illegal_instruction_combination (cpu); TRACE_BRANCH (cpu, pc, newpc, -1, "JUMP (Preg)"); SET_PCREG (newpc); BFIN_CPU_STATE.did_jump = true; PROFILE_BRANCH_TAKEN (cpu); CYCLE_DELAY = 5; } else if (prgfunc == 6 && poprnd < 8) { bu32 newpc = PREG (poprnd); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_branch); TRACE_INSN (cpu, "CALL (%s);", get_preg_name (poprnd)); IFETCH_CHECK (newpc); if (INSN_LEN == 8) illegal_instruction_combination (cpu); TRACE_BRANCH (cpu, pc, newpc, -1, "CALL (Preg)"); /* If we're at the end of a hardware loop, RETS is going to be the top of the loop rather than the next instruction. */ SET_RETSREG (hwloop_get_next_pc (cpu, pc, 2)); SET_PCREG (newpc); BFIN_CPU_STATE.did_jump = true; PROFILE_BRANCH_TAKEN (cpu); CYCLE_DELAY = 5; } else if (prgfunc == 7 && poprnd < 8) { bu32 newpc = pc + PREG (poprnd); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_branch); TRACE_INSN (cpu, "CALL (PC + %s);", get_preg_name (poprnd)); IFETCH_CHECK (newpc); if (INSN_LEN == 8) illegal_instruction_combination (cpu); TRACE_BRANCH (cpu, pc, newpc, -1, "CALL (PC + Preg)"); SET_RETSREG (hwloop_get_next_pc (cpu, pc, 2)); SET_PCREG (newpc); BFIN_CPU_STATE.did_jump = true; PROFILE_BRANCH_TAKEN (cpu); CYCLE_DELAY = 5; } else if (prgfunc == 8 && poprnd < 8) { bu32 newpc = pc + PREG (poprnd); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_branch); TRACE_INSN (cpu, "JUMP (PC + %s);", get_preg_name (poprnd)); IFETCH_CHECK (newpc); if (INSN_LEN == 8) illegal_instruction_combination (cpu); TRACE_BRANCH (cpu, pc, newpc, -1, "JUMP (PC + Preg)"); SET_PCREG (newpc); BFIN_CPU_STATE.did_jump = true; PROFILE_BRANCH_TAKEN (cpu); CYCLE_DELAY = 5; } else if (prgfunc == 9) { int raise = uimm4 (poprnd); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_cec); TRACE_INSN (cpu, "RAISE %s;", uimm4_str (raise)); if (INSN_LEN == 8) illegal_instruction_combination (cpu); cec_require_supervisor (cpu); if (raise == IVG_IVHW) cec_hwerr (cpu, HWERR_RAISE_5); else cec_latch (cpu, raise); CYCLE_DELAY = 3; /* XXX: Only if IVG is unmasked. */ } else if (prgfunc == 10) { int excpt = uimm4 (poprnd); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_cec); TRACE_INSN (cpu, "EXCPT %s;", uimm4_str (excpt)); if (INSN_LEN == 8) illegal_instruction_combination (cpu); cec_exception (cpu, excpt); CYCLE_DELAY = 3; } else if (prgfunc == 11 && poprnd < 6) { bu32 addr = PREG (poprnd); bu8 byte; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ProgCtrl_atomic); TRACE_INSN (cpu, "TESTSET (%s);", get_preg_name (poprnd)); if (INSN_LEN == 8) illegal_instruction_combination (cpu); byte = GET_WORD (addr); SET_CCREG (byte == 0); PUT_BYTE (addr, byte | 0x80); /* Also includes memory stalls, but we don't model that. */ CYCLE_DELAY = 2; } else illegal_instruction (cpu); } static void decode_CaCTRL_0 (SIM_CPU *cpu, bu16 iw0) { /* CaCTRL +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 |.a.|.op....|.reg.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int a = ((iw0 >> CaCTRL_a_bits) & CaCTRL_a_mask); int op = ((iw0 >> CaCTRL_op_bits) & CaCTRL_op_mask); int reg = ((iw0 >> CaCTRL_reg_bits) & CaCTRL_reg_mask); bu32 preg = PREG (reg); const char * const sinsn[] = { "PREFETCH", "FLUSHINV", "FLUSH", "IFLUSH", }; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_CaCTRL); TRACE_EXTRACT (cpu, "%s: a:%i op:%i reg:%i", __func__, a, op, reg); TRACE_INSN (cpu, "%s [%s%s];", sinsn[op], get_preg_name (reg), a ? "++" : ""); if (INSN_LEN == 8) /* None of these can be part of a parallel instruction. */ illegal_instruction_combination (cpu); /* No cache simulation, so these are (mostly) all NOPs. XXX: The hardware takes care of masking to cache lines, but need to check behavior of the post increment. Should we be aligning the value to the cache line before adding the cache line size, or do we just add the cache line size ? */ if (op == 0) { /* PREFETCH */ mmu_check_cache_addr (cpu, preg, false, false); } else if (op == 1) { /* FLUSHINV */ mmu_check_cache_addr (cpu, preg, true, false); } else if (op == 2) { /* FLUSH */ mmu_check_cache_addr (cpu, preg, true, false); } else if (op == 3) { /* IFLUSH */ mmu_check_cache_addr (cpu, preg, false, true); } if (a) SET_PREG (reg, preg + BFIN_L1_CACHE_BYTES); } static void decode_PushPopReg_0 (SIM_CPU *cpu, bu16 iw0) { /* PushPopReg +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |.W.|.grp.......|.reg.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int W = ((iw0 >> PushPopReg_W_bits) & PushPopReg_W_mask); int grp = ((iw0 >> PushPopReg_grp_bits) & PushPopReg_grp_mask); int reg = ((iw0 >> PushPopReg_reg_bits) & PushPopReg_reg_mask); const char *reg_name = get_allreg_name (grp, reg); bu32 value; bu32 sp = SPREG; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_PushPopReg); TRACE_EXTRACT (cpu, "%s: W:%i grp:%i reg:%i", __func__, W, grp, reg); TRACE_DECODE (cpu, "%s: reg:%s", __func__, reg_name); /* Can't push/pop reserved registers */ if (reg_is_reserved (grp, reg)) illegal_instruction (cpu); if (W == 0) { /* Dreg and Preg are not supported by this instruction. */ if (grp == 0 || grp == 1) illegal_instruction (cpu); TRACE_INSN (cpu, "%s = [SP++];", reg_name); /* Can't pop USP while in userspace. */ if (INSN_LEN == 8 || (grp == 7 && reg == 0 && cec_is_user_mode(cpu))) illegal_instruction_combination (cpu); /* XXX: The valid register check is in reg_write(), so we might incorrectly do a GET_LONG() here ... */ value = GET_LONG (sp); reg_write (cpu, grp, reg, value); if (grp == 7 && reg == 3) cec_pop_reti (cpu); sp += 4; } else { TRACE_INSN (cpu, "[--SP] = %s;", reg_name); /* Can't push SP. */ if (INSN_LEN == 8 || (grp == 1 && reg == 6)) illegal_instruction_combination (cpu); sp -= 4; value = reg_read (cpu, grp, reg); if (grp == 7 && reg == 3) cec_push_reti (cpu); PUT_LONG (sp, value); } /* Note: SP update must be delayed until after all reads/writes; see comments in decode_PushPopMultiple_0() for more info. */ SET_SPREG (sp); } static void decode_PushPopMultiple_0 (SIM_CPU *cpu, bu16 iw0) { /* PushPopMultiple +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | 1 | 0 |.d.|.p.|.W.|.dr........|.pr........| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int p = ((iw0 >> PushPopMultiple_p_bits) & PushPopMultiple_p_mask); int d = ((iw0 >> PushPopMultiple_d_bits) & PushPopMultiple_d_mask); int W = ((iw0 >> PushPopMultiple_W_bits) & PushPopMultiple_W_mask); int dr = ((iw0 >> PushPopMultiple_dr_bits) & PushPopMultiple_dr_mask); int pr = ((iw0 >> PushPopMultiple_pr_bits) & PushPopMultiple_pr_mask); int i; bu32 sp = SPREG; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_PushPopMultiple); TRACE_EXTRACT (cpu, "%s: d:%i p:%i W:%i dr:%i pr:%i", __func__, d, p, W, dr, pr); if ((d == 0 && p == 0) || (p && imm5 (pr) > 5) || (d && !p && pr) || (p && !d && dr)) illegal_instruction (cpu); if (W == 1) { if (d && p) TRACE_INSN (cpu, "[--SP] = (R7:%i, P5:%i);", dr, pr); else if (d) TRACE_INSN (cpu, "[--SP] = (R7:%i);", dr); else TRACE_INSN (cpu, "[--SP] = (P5:%i);", pr); if (d) for (i = dr; i < 8; i++) { sp -= 4; PUT_LONG (sp, DREG (i)); } if (p) for (i = pr; i < 6; i++) { sp -= 4; PUT_LONG (sp, PREG (i)); } CYCLE_DELAY = 14; } else { if (d && p) TRACE_INSN (cpu, "(R7:%i, P5:%i) = [SP++];", dr, pr); else if (d) TRACE_INSN (cpu, "(R7:%i) = [SP++];", dr); else TRACE_INSN (cpu, "(P5:%i) = [SP++];", pr); if (p) for (i = 5; i >= pr; i--) { SET_PREG (i, GET_LONG (sp)); sp += 4; } if (d) for (i = 7; i >= dr; i--) { SET_DREG (i, GET_LONG (sp)); sp += 4; } CYCLE_DELAY = 11; } /* Note: SP update must be delayed until after all reads/writes so that if an exception does occur, the insn may be re-executed as the SP has not yet changed. */ SET_SPREG (sp); } static void decode_ccMV_0 (SIM_CPU *cpu, bu16 iw0) { /* ccMV +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | 1 | 1 |.T.|.d.|.s.|.dst.......|.src.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int s = ((iw0 >> CCmv_s_bits) & CCmv_s_mask); int d = ((iw0 >> CCmv_d_bits) & CCmv_d_mask); int T = ((iw0 >> CCmv_T_bits) & CCmv_T_mask); int src = ((iw0 >> CCmv_src_bits) & CCmv_src_mask); int dst = ((iw0 >> CCmv_dst_bits) & CCmv_dst_mask); int cond = T ? CCREG : ! CCREG; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ccMV); TRACE_EXTRACT (cpu, "%s: T:%i d:%i s:%i dst:%i src:%i", __func__, T, d, s, dst, src); TRACE_INSN (cpu, "IF %sCC %s = %s;", T ? "" : "! ", get_allreg_name (d, dst), get_allreg_name (s, src)); if (INSN_LEN == 8) illegal_instruction_combination (cpu); if (cond) reg_write (cpu, d, dst, reg_read (cpu, s, src)); } static void decode_CCflag_0 (SIM_CPU *cpu, bu16 iw0) { /* CCflag +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 0 | 0 | 0 | 1 |.I.|.opc.......|.G.|.y.........|.x.........| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int x = ((iw0 >> CCflag_x_bits) & CCflag_x_mask); int y = ((iw0 >> CCflag_y_bits) & CCflag_y_mask); int I = ((iw0 >> CCflag_I_bits) & CCflag_I_mask); int G = ((iw0 >> CCflag_G_bits) & CCflag_G_mask); int opc = ((iw0 >> CCflag_opc_bits) & CCflag_opc_mask); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_CCflag); TRACE_EXTRACT (cpu, "%s: I:%i opc:%i G:%i y:%i x:%i", __func__, I, opc, G, y, x); if (opc > 4) { bs64 acc0 = get_extended_acc (cpu, 0); bs64 acc1 = get_extended_acc (cpu, 1); bs64 diff = acc0 - acc1; if (x != 0 || y != 0) illegal_instruction (cpu); if (opc == 5 && I == 0 && G == 0) { TRACE_INSN (cpu, "CC = A0 == A1;"); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_CCREG (acc0 == acc1); } else if (opc == 6 && I == 0 && G == 0) { TRACE_INSN (cpu, "CC = A0 < A1"); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_CCREG (acc0 < acc1); } else if (opc == 7 && I == 0 && G == 0) { TRACE_INSN (cpu, "CC = A0 <= A1"); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_CCREG (acc0 <= acc1); } else illegal_instruction (cpu); SET_ASTATREG (az, diff == 0); SET_ASTATREG (an, diff < 0); SET_ASTATREG (ac0, (bu40)acc1 <= (bu40)acc0); } else { int issigned = opc < 3; const char *sign = issigned ? "" : " (IU)"; bu32 srcop = G ? PREG (x) : DREG (x); char s = G ? 'P' : 'R'; bu32 dstop = I ? (issigned ? imm3 (y) : uimm3 (y)) : G ? PREG (y) : DREG (y); const char *op; char d = G ? 'P' : 'R'; int flgs = srcop >> 31; int flgo = dstop >> 31; bu32 result = srcop - dstop; int cc; int flgn = result >> 31; int overflow = (flgs ^ flgo) & (flgn ^ flgs); int az = result == 0; int ac0 = dstop <= srcop; int an; if (issigned) an = (flgn && !overflow) || (!flgn && overflow); else an = dstop > srcop; switch (opc) { default: /* Shutup useless gcc warnings. */ case 0: /* signed */ op = "=="; cc = az; break; case 1: /* signed */ op = "<"; cc = an; break; case 2: /* signed */ op = "<="; cc = an || az; break; case 3: /* unsigned */ op = "<"; cc = !ac0; break; case 4: /* unsigned */ op = "<="; cc = !ac0 || az; break; } if (I) TRACE_INSN (cpu, "CC = %c%i %s %s%s;", s, x, op, issigned ? imm3_str (y) : uimm3_str (y), sign); else { TRACE_DECODE (cpu, "%s %c%i:%x %c%i:%x", __func__, s, x, srcop, d, y, dstop); TRACE_INSN (cpu, "CC = %c%i %s %c%i%s;", s, x, op, d, y, sign); } SET_CCREG (cc); /* Pointer compares only touch CC. */ if (!G) { SET_ASTATREG (az, az); SET_ASTATREG (an, an); SET_ASTATREG (ac0, ac0); } } } static void decode_CC2dreg_0 (SIM_CPU *cpu, bu16 iw0) { /* CC2dreg +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |.op....|.reg.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int op = ((iw0 >> CC2dreg_op_bits) & CC2dreg_op_mask); int reg = ((iw0 >> CC2dreg_reg_bits) & CC2dreg_reg_mask); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_CC2dreg); TRACE_EXTRACT (cpu, "%s: op:%i reg:%i", __func__, op, reg); if (op == 0) { TRACE_INSN (cpu, "R%i = CC;", reg); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_DREG (reg, CCREG); } else if (op == 1) { TRACE_INSN (cpu, "CC = R%i;", reg); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_CCREG (DREG (reg) != 0); } else if (op == 3 && reg == 0) { TRACE_INSN (cpu, "CC = !CC;"); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_CCREG (!CCREG); } else illegal_instruction (cpu); } static void decode_CC2stat_0 (SIM_CPU *cpu, bu16 iw0) { /* CC2stat +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |.D.|.op....|.cbit..............| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int D = ((iw0 >> CC2stat_D_bits) & CC2stat_D_mask); int op = ((iw0 >> CC2stat_op_bits) & CC2stat_op_mask); int cbit = ((iw0 >> CC2stat_cbit_bits) & CC2stat_cbit_mask); bu32 pval; const char * const op_names[] = { "", "|", "&", "^" } ; const char *astat_name; const char * const astat_names[32] = { [ 0] = "AZ", [ 1] = "AN", [ 2] = "AC0_COPY", [ 3] = "V_COPY", [ 5] = "CC", [ 6] = "AQ", [ 8] = "RND_MOD", [12] = "AC0", [13] = "AC1", [16] = "AV0", [17] = "AV0S", [18] = "AV1", [19] = "AV1S", [24] = "V", [25] = "VS", }; astat_name = astat_names[cbit]; if (!astat_name) { static char astat_bit[12]; sprintf (astat_bit, "ASTAT[%i]", cbit); astat_name = astat_bit; } PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_CC2stat); TRACE_EXTRACT (cpu, "%s: D:%i op:%i cbit:%i", __func__, D, op, cbit); TRACE_INSN (cpu, "%s %s= %s;", D ? astat_name : "CC", op_names[op], D ? "CC" : astat_name); /* CC = CC; is invalid. */ if (cbit == 5) illegal_instruction (cpu); if (INSN_LEN == 8) illegal_instruction_combination (cpu); pval = !!(ASTAT & (1 << cbit)); if (D == 0) switch (op) { case 0: SET_CCREG (pval); break; case 1: SET_CCREG (CCREG | pval); break; case 2: SET_CCREG (CCREG & pval); break; case 3: SET_CCREG (CCREG ^ pval); break; } else { switch (op) { case 0: pval = CCREG; break; case 1: pval |= CCREG; break; case 2: pval &= CCREG; break; case 3: pval ^= CCREG; break; } if (astat_names[cbit]) TRACE_REGISTER (cpu, "wrote ASTAT[%s] = %i", astat_name, pval); else TRACE_REGISTER (cpu, "wrote %s = %i", astat_name, pval); SET_ASTAT ((ASTAT & ~(1 << cbit)) | (pval << cbit)); } } static void decode_BRCC_0 (SIM_CPU *cpu, bu16 iw0, bu32 pc) { /* BRCC +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 0 | 0 | 1 |.T.|.B.|.offset................................| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int B = ((iw0 >> BRCC_B_bits) & BRCC_B_mask); int T = ((iw0 >> BRCC_T_bits) & BRCC_T_mask); int offset = ((iw0 >> BRCC_offset_bits) & BRCC_offset_mask); int cond = T ? CCREG : ! CCREG; int pcrel = pcrel10 (offset); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_BRCC); TRACE_EXTRACT (cpu, "%s: T:%i B:%i offset:%#x", __func__, T, B, offset); TRACE_DECODE (cpu, "%s: pcrel10:%#x", __func__, pcrel); TRACE_INSN (cpu, "IF %sCC JUMP %#x%s;", T ? "" : "! ", pcrel, B ? " (bp)" : ""); if (INSN_LEN == 8) illegal_instruction_combination (cpu); if (cond) { bu32 newpc = pc + pcrel; TRACE_BRANCH (cpu, pc, newpc, -1, "Conditional JUMP"); SET_PCREG (newpc); BFIN_CPU_STATE.did_jump = true; PROFILE_BRANCH_TAKEN (cpu); CYCLE_DELAY = B ? 5 : 9; } else { PROFILE_BRANCH_UNTAKEN (cpu); CYCLE_DELAY = B ? 9 : 1; } } static void decode_UJUMP_0 (SIM_CPU *cpu, bu16 iw0, bu32 pc) { /* UJUMP +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 0 | 1 | 0 |.offset........................................| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int offset = ((iw0 >> UJump_offset_bits) & UJump_offset_mask); int pcrel = pcrel12 (offset); bu32 newpc = pc + pcrel; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_UJUMP); TRACE_EXTRACT (cpu, "%s: offset:%#x", __func__, offset); TRACE_DECODE (cpu, "%s: pcrel12:%#x", __func__, pcrel); TRACE_INSN (cpu, "JUMP.S %#x;", pcrel); if (INSN_LEN == 8) illegal_instruction_combination (cpu); TRACE_BRANCH (cpu, pc, newpc, -1, "JUMP.S"); SET_PCREG (newpc); BFIN_CPU_STATE.did_jump = true; PROFILE_BRANCH_TAKEN (cpu); CYCLE_DELAY = 5; } static void decode_REGMV_0 (SIM_CPU *cpu, bu16 iw0) { /* REGMV +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 0 | 1 | 1 |.gd........|.gs........|.dst.......|.src.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int gs = ((iw0 >> RegMv_gs_bits) & RegMv_gs_mask); int gd = ((iw0 >> RegMv_gd_bits) & RegMv_gd_mask); int src = ((iw0 >> RegMv_src_bits) & RegMv_src_mask); int dst = ((iw0 >> RegMv_dst_bits) & RegMv_dst_mask); const char *srcreg_name = get_allreg_name (gs, src); const char *dstreg_name = get_allreg_name (gd, dst); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_REGMV); TRACE_EXTRACT (cpu, "%s: gd:%i gs:%i dst:%i src:%i", __func__, gd, gs, dst, src); TRACE_DECODE (cpu, "%s: dst:%s src:%s", __func__, dstreg_name, srcreg_name); TRACE_INSN (cpu, "%s = %s;", dstreg_name, srcreg_name); /* Reserved slots cannot be a src/dst. */ if (reg_is_reserved (gs, src) || reg_is_reserved (gd, dst)) goto invalid_move; /* Standard register moves. */ if ((gs < 2) /* Dregs/Pregs src */ || (gd < 2) /* Dregs/Pregs dst */ || (gs == 4 && src < 4) /* Accumulators src */ || (gd == 4 && dst < 4 && (gs < 4)) /* Accumulators dst */ || (gs == 7 && src == 7 && !(gd == 4 && dst < 4)) /* EMUDAT src */ || (gd == 7 && dst == 7)) /* EMUDAT dst */ goto valid_move; /* dareg = dareg (IMBL) */ if (gs < 4 && gd < 4) goto valid_move; /* USP can be src to sysregs, but not dagregs. */ if ((gs == 7 && src == 0) && (gd >= 4)) goto valid_move; /* USP can move between genregs (only check Accumulators). */ if (((gs == 7 && src == 0) && (gd == 4 && dst < 4)) || ((gd == 7 && dst == 0) && (gs == 4 && src < 4))) goto valid_move; /* Still here ? Invalid reg pair. */ invalid_move: illegal_instruction (cpu); valid_move: reg_write (cpu, gd, dst, reg_read (cpu, gs, src)); } static void decode_ALU2op_0 (SIM_CPU *cpu, bu16 iw0) { /* ALU2op +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 1 | 0 | 0 | 0 | 0 |.opc...........|.src.......|.dst.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int src = ((iw0 >> ALU2op_src_bits) & ALU2op_src_mask); int opc = ((iw0 >> ALU2op_opc_bits) & ALU2op_opc_mask); int dst = ((iw0 >> ALU2op_dst_bits) & ALU2op_dst_mask); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_ALU2op); TRACE_EXTRACT (cpu, "%s: opc:%i src:%i dst:%i", __func__, opc, src, dst); if (opc == 0) { TRACE_INSN (cpu, "R%i >>>= R%i;", dst, src); SET_DREG (dst, ashiftrt (cpu, DREG (dst), DREG (src), 32)); } else if (opc == 1) { bu32 val; TRACE_INSN (cpu, "R%i >>= R%i;", dst, src); if (DREG (src) <= 0x1F) val = lshiftrt (cpu, DREG (dst), DREG (src), 32); else val = 0; SET_DREG (dst, val); } else if (opc == 2) { TRACE_INSN (cpu, "R%i <<= R%i;", dst, src); SET_DREG (dst, lshift (cpu, DREG (dst), DREG (src), 32, 0)); } else if (opc == 3) { TRACE_INSN (cpu, "R%i *= R%i;", dst, src); SET_DREG (dst, DREG (dst) * DREG (src)); CYCLE_DELAY = 3; } else if (opc == 4) { TRACE_INSN (cpu, "R%i = (R%i + R%i) << 1;", dst, dst, src); SET_DREG (dst, add_and_shift (cpu, DREG (dst), DREG (src), 1)); } else if (opc == 5) { TRACE_INSN (cpu, "R%i = (R%i + R%i) << 2;", dst, dst, src); SET_DREG (dst, add_and_shift (cpu, DREG (dst), DREG (src), 2)); } else if (opc == 8) { TRACE_INSN (cpu, "DIVQ ( R%i, R%i );", dst, src); SET_DREG (dst, divq (cpu, DREG (dst), (bu16)DREG (src))); } else if (opc == 9) { TRACE_INSN (cpu, "DIVS ( R%i, R%i );", dst, src); SET_DREG (dst, divs (cpu, DREG (dst), (bu16)DREG (src))); } else if (opc == 10) { TRACE_INSN (cpu, "R%i = R%i.L (X);", dst, src); SET_DREG (dst, (bs32) (bs16) DREG (src)); setflags_logical (cpu, DREG (dst)); } else if (opc == 11) { TRACE_INSN (cpu, "R%i = R%i.L (Z);", dst, src); SET_DREG (dst, (bu32) (bu16) DREG (src)); setflags_logical (cpu, DREG (dst)); } else if (opc == 12) { TRACE_INSN (cpu, "R%i = R%i.B (X);", dst, src); SET_DREG (dst, (bs32) (bs8) DREG (src)); setflags_logical (cpu, DREG (dst)); } else if (opc == 13) { TRACE_INSN (cpu, "R%i = R%i.B (Z);", dst, src); SET_DREG (dst, (bu32) (bu8) DREG (src)); setflags_logical (cpu, DREG (dst)); } else if (opc == 14) { bu32 val = DREG (src); TRACE_INSN (cpu, "R%i = - R%i;", dst, src); SET_DREG (dst, -val); setflags_nz (cpu, DREG (dst)); SET_ASTATREG (v, val == 0x80000000); if (ASTATREG (v)) SET_ASTATREG (vs, 1); SET_ASTATREG (ac0, val == 0x0); /* XXX: Documentation isn't entirely clear about av0 and av1. */ } else if (opc == 15) { TRACE_INSN (cpu, "R%i = ~ R%i;", dst, src); SET_DREG (dst, ~DREG (src)); setflags_logical (cpu, DREG (dst)); } else illegal_instruction (cpu); } static void decode_PTR2op_0 (SIM_CPU *cpu, bu16 iw0) { /* PTR2op +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 1 | 0 | 0 | 0 | 1 | 0 |.opc.......|.src.......|.dst.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int src = ((iw0 >> PTR2op_src_bits) & PTR2op_dst_mask); int opc = ((iw0 >> PTR2op_opc_bits) & PTR2op_opc_mask); int dst = ((iw0 >> PTR2op_dst_bits) & PTR2op_dst_mask); const char *src_name = get_preg_name (src); const char *dst_name = get_preg_name (dst); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_PTR2op); TRACE_EXTRACT (cpu, "%s: opc:%i src:%i dst:%i", __func__, opc, src, dst); if (opc == 0) { TRACE_INSN (cpu, "%s -= %s", dst_name, src_name); SET_PREG (dst, PREG (dst) - PREG (src)); } else if (opc == 1) { TRACE_INSN (cpu, "%s = %s << 2", dst_name, src_name); SET_PREG (dst, PREG (src) << 2); } else if (opc == 3) { TRACE_INSN (cpu, "%s = %s >> 2", dst_name, src_name); SET_PREG (dst, PREG (src) >> 2); } else if (opc == 4) { TRACE_INSN (cpu, "%s = %s >> 1", dst_name, src_name); SET_PREG (dst, PREG (src) >> 1); } else if (opc == 5) { TRACE_INSN (cpu, "%s += %s (BREV)", dst_name, src_name); SET_PREG (dst, add_brev (PREG (dst), PREG (src))); } else if (opc == 6) { TRACE_INSN (cpu, "%s = (%s + %s) << 1", dst_name, dst_name, src_name); SET_PREG (dst, (PREG (dst) + PREG (src)) << 1); } else if (opc == 7) { TRACE_INSN (cpu, "%s = (%s + %s) << 2", dst_name, dst_name, src_name); SET_PREG (dst, (PREG (dst) + PREG (src)) << 2); } else illegal_instruction (cpu); } static void decode_LOGI2op_0 (SIM_CPU *cpu, bu16 iw0) { /* LOGI2op +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 1 | 0 | 0 | 1 |.opc.......|.src...............|.dst.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int src = ((iw0 >> LOGI2op_src_bits) & LOGI2op_src_mask); int opc = ((iw0 >> LOGI2op_opc_bits) & LOGI2op_opc_mask); int dst = ((iw0 >> LOGI2op_dst_bits) & LOGI2op_dst_mask); int uimm = uimm5 (src); const char *uimm_str = uimm5_str (uimm); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_LOGI2op); TRACE_EXTRACT (cpu, "%s: opc:%i src:%i dst:%i", __func__, opc, src, dst); TRACE_DECODE (cpu, "%s: uimm5:%#x", __func__, uimm); if (opc == 0) { TRACE_INSN (cpu, "CC = ! BITTST (R%i, %s);", dst, uimm_str); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_CCREG ((~DREG (dst) >> uimm) & 1); } else if (opc == 1) { TRACE_INSN (cpu, "CC = BITTST (R%i, %s);", dst, uimm_str); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_CCREG ((DREG (dst) >> uimm) & 1); } else if (opc == 2) { TRACE_INSN (cpu, "BITSET (R%i, %s);", dst, uimm_str); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_DREG (dst, DREG (dst) | (1 << uimm)); setflags_logical (cpu, DREG (dst)); } else if (opc == 3) { TRACE_INSN (cpu, "BITTGL (R%i, %s);", dst, uimm_str); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_DREG (dst, DREG (dst) ^ (1 << uimm)); setflags_logical (cpu, DREG (dst)); } else if (opc == 4) { TRACE_INSN (cpu, "BITCLR (R%i, %s);", dst, uimm_str); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_DREG (dst, DREG (dst) & ~(1 << uimm)); setflags_logical (cpu, DREG (dst)); } else if (opc == 5) { TRACE_INSN (cpu, "R%i >>>= %s;", dst, uimm_str); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_DREG (dst, ashiftrt (cpu, DREG (dst), uimm, 32)); } else if (opc == 6) { TRACE_INSN (cpu, "R%i >>= %s;", dst, uimm_str); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_DREG (dst, lshiftrt (cpu, DREG (dst), uimm, 32)); } else if (opc == 7) { TRACE_INSN (cpu, "R%i <<= %s;", dst, uimm_str); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_DREG (dst, lshift (cpu, DREG (dst), uimm, 32, 0)); } } static void decode_COMP3op_0 (SIM_CPU *cpu, bu16 iw0) { /* COMP3op +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 1 | 0 | 1 |.opc.......|.dst.......|.src1......|.src0......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int opc = ((iw0 >> COMP3op_opc_bits) & COMP3op_opc_mask); int dst = ((iw0 >> COMP3op_dst_bits) & COMP3op_dst_mask); int src0 = ((iw0 >> COMP3op_src0_bits) & COMP3op_src0_mask); int src1 = ((iw0 >> COMP3op_src1_bits) & COMP3op_src1_mask); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_COMP3op); TRACE_EXTRACT (cpu, "%s: opc:%i dst:%i src1:%i src0:%i", __func__, opc, dst, src1, src0); if (opc == 0) { TRACE_INSN (cpu, "R%i = R%i + R%i;", dst, src0, src1); SET_DREG (dst, add32 (cpu, DREG (src0), DREG (src1), 1, 0)); } else if (opc == 1) { TRACE_INSN (cpu, "R%i = R%i - R%i;", dst, src0, src1); SET_DREG (dst, sub32 (cpu, DREG (src0), DREG (src1), 1, 0, 0)); } else if (opc == 2) { TRACE_INSN (cpu, "R%i = R%i & R%i;", dst, src0, src1); SET_DREG (dst, DREG (src0) & DREG (src1)); setflags_logical (cpu, DREG (dst)); } else if (opc == 3) { TRACE_INSN (cpu, "R%i = R%i | R%i;", dst, src0, src1); SET_DREG (dst, DREG (src0) | DREG (src1)); setflags_logical (cpu, DREG (dst)); } else if (opc == 4) { TRACE_INSN (cpu, "R%i = R%i ^ R%i;", dst, src0, src1); SET_DREG (dst, DREG (src0) ^ DREG (src1)); setflags_logical (cpu, DREG (dst)); } else { int shift = opc - 5; const char *dst_name = get_preg_name (dst); const char *src0_name = get_preg_name (src0); const char *src1_name = get_preg_name (src1); /* If src0 == src1 this is disassembled as a shift by 1, but this distinction doesn't matter for our purposes. */ if (shift) TRACE_INSN (cpu, "%s = (%s + %s) << %#x;", dst_name, src0_name, src1_name, shift); else TRACE_INSN (cpu, "%s = %s + %s", dst_name, src0_name, src1_name); SET_PREG (dst, PREG (src0) + (PREG (src1) << shift)); } } static void decode_COMPI2opD_0 (SIM_CPU *cpu, bu16 iw0) { /* COMPI2opD +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 1 | 1 | 0 | 0 |.op|..src......................|.dst.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int op = ((iw0 >> COMPI2opD_op_bits) & COMPI2opD_op_mask); int dst = ((iw0 >> COMPI2opD_dst_bits) & COMPI2opD_dst_mask); int src = ((iw0 >> COMPI2opD_src_bits) & COMPI2opD_src_mask); int imm = imm7 (src); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_COMPI2opD); TRACE_EXTRACT (cpu, "%s: op:%i src:%i dst:%i", __func__, op, src, dst); TRACE_DECODE (cpu, "%s: imm7:%#x", __func__, imm); if (op == 0) { TRACE_INSN (cpu, "R%i = %s (X);", dst, imm7_str (imm)); SET_DREG (dst, imm); } else if (op == 1) { TRACE_INSN (cpu, "R%i += %s;", dst, imm7_str (imm)); SET_DREG (dst, add32 (cpu, DREG (dst), imm, 1, 0)); } } static void decode_COMPI2opP_0 (SIM_CPU *cpu, bu16 iw0) { /* COMPI2opP +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 0 | 1 | 1 | 0 | 1 |.op|.src.......................|.dst.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int op = ((iw0 >> COMPI2opP_op_bits) & COMPI2opP_op_mask); int src = ((iw0 >> COMPI2opP_src_bits) & COMPI2opP_src_mask); int dst = ((iw0 >> COMPI2opP_dst_bits) & COMPI2opP_dst_mask); int imm = imm7 (src); const char *dst_name = get_preg_name (dst); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_COMPI2opP); TRACE_EXTRACT (cpu, "%s: op:%i src:%i dst:%i", __func__, op, src, dst); TRACE_DECODE (cpu, "%s: imm:%#x", __func__, imm); if (op == 0) { TRACE_INSN (cpu, "%s = %s;", dst_name, imm7_str (imm)); SET_PREG (dst, imm); } else if (op == 1) { TRACE_INSN (cpu, "%s += %s;", dst_name, imm7_str (imm)); SET_PREG (dst, PREG (dst) + imm); } } static void decode_LDSTpmod_0 (SIM_CPU *cpu, bu16 iw0) { /* LDSTpmod +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 0 | 0 | 0 |.W.|.aop...|.reg.......|.idx.......|.ptr.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int W = ((iw0 >> LDSTpmod_W_bits) & LDSTpmod_W_mask); int aop = ((iw0 >> LDSTpmod_aop_bits) & LDSTpmod_aop_mask); int idx = ((iw0 >> LDSTpmod_idx_bits) & LDSTpmod_idx_mask); int ptr = ((iw0 >> LDSTpmod_ptr_bits) & LDSTpmod_ptr_mask); int reg = ((iw0 >> LDSTpmod_reg_bits) & LDSTpmod_reg_mask); const char *ptr_name = get_preg_name (ptr); const char *idx_name = get_preg_name (idx); bu32 addr, val; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_LDSTpmod); TRACE_EXTRACT (cpu, "%s: W:%i aop:%i reg:%i idx:%i ptr:%i", __func__, W, aop, reg, idx, ptr); if (aop == 1 && W == 0 && idx == ptr) { TRACE_INSN (cpu, "R%i.L = W[%s];", reg, ptr_name); addr = PREG (ptr); val = GET_WORD (addr); STORE (DREG (reg), (DREG (reg) & 0xFFFF0000) | val); } else if (aop == 2 && W == 0 && idx == ptr) { TRACE_INSN (cpu, "R%i.H = W[%s];", reg, ptr_name); addr = PREG (ptr); val = GET_WORD (addr); STORE (DREG (reg), (DREG (reg) & 0xFFFF) | (val << 16)); } else if (aop == 1 && W == 1 && idx == ptr) { TRACE_INSN (cpu, "W[%s] = R%i.L;", ptr_name, reg); addr = PREG (ptr); PUT_WORD (addr, DREG (reg)); } else if (aop == 2 && W == 1 && idx == ptr) { TRACE_INSN (cpu, "W[%s] = R%i.H;", ptr_name, reg); addr = PREG (ptr); PUT_WORD (addr, DREG (reg) >> 16); } else if (aop == 0 && W == 0) { TRACE_INSN (cpu, "R%i = [%s ++ %s];", reg, ptr_name, idx_name); addr = PREG (ptr); val = GET_LONG (addr); STORE (DREG (reg), val); if (ptr != idx) STORE (PREG (ptr), addr + PREG (idx)); } else if (aop == 1 && W == 0) { TRACE_INSN (cpu, "R%i.L = W[%s ++ %s];", reg, ptr_name, idx_name); addr = PREG (ptr); val = GET_WORD (addr); STORE (DREG (reg), (DREG (reg) & 0xFFFF0000) | val); if (ptr != idx) STORE (PREG (ptr), addr + PREG (idx)); } else if (aop == 2 && W == 0) { TRACE_INSN (cpu, "R%i.H = W[%s ++ %s];", reg, ptr_name, idx_name); addr = PREG (ptr); val = GET_WORD (addr); STORE (DREG (reg), (DREG (reg) & 0xFFFF) | (val << 16)); if (ptr != idx) STORE (PREG (ptr), addr + PREG (idx)); } else if (aop == 3 && W == 0) { TRACE_INSN (cpu, "R%i = W[%s ++ %s] (Z);", reg, ptr_name, idx_name); addr = PREG (ptr); val = GET_WORD (addr); STORE (DREG (reg), val); if (ptr != idx) STORE (PREG (ptr), addr + PREG (idx)); } else if (aop == 3 && W == 1) { TRACE_INSN (cpu, "R%i = W[%s ++ %s] (X);", reg, ptr_name, idx_name); addr = PREG (ptr); val = GET_WORD (addr); STORE (DREG (reg), (bs32) (bs16) val); if (ptr != idx) STORE (PREG (ptr), addr + PREG (idx)); } else if (aop == 0 && W == 1) { TRACE_INSN (cpu, "[%s ++ %s] = R%i;", ptr_name, idx_name, reg); addr = PREG (ptr); PUT_LONG (addr, DREG (reg)); if (ptr != idx) STORE (PREG (ptr), addr + PREG (idx)); } else if (aop == 1 && W == 1) { TRACE_INSN (cpu, "W[%s ++ %s] = R%i.L;", ptr_name, idx_name, reg); addr = PREG (ptr); PUT_WORD (addr, DREG (reg)); if (ptr != idx) STORE (PREG (ptr), addr + PREG (idx)); } else if (aop == 2 && W == 1) { TRACE_INSN (cpu, "W[%s ++ %s] = R%i.H;", ptr_name, idx_name, reg); addr = PREG (ptr); PUT_WORD (addr, DREG (reg) >> 16); if (ptr != idx) STORE (PREG (ptr), addr + PREG (idx)); } else illegal_instruction (cpu); } static void decode_dagMODim_0 (SIM_CPU *cpu, bu16 iw0) { /* dagMODim +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 |.br| 1 | 1 |.op|.m.....|.i.....| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int i = ((iw0 >> DagMODim_i_bits) & DagMODim_i_mask); int m = ((iw0 >> DagMODim_m_bits) & DagMODim_m_mask); int br = ((iw0 >> DagMODim_br_bits) & DagMODim_br_mask); int op = ((iw0 >> DagMODim_op_bits) & DagMODim_op_mask); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_dagMODim); TRACE_EXTRACT (cpu, "%s: br:%i op:%i m:%i i:%i", __func__, br, op, m, i); if (op == 0 && br == 1) { TRACE_INSN (cpu, "I%i += M%i (BREV);", i, m); SET_IREG (i, add_brev (IREG (i), MREG (m))); } else if (op == 0) { TRACE_INSN (cpu, "I%i += M%i;", i, m); dagadd (cpu, i, MREG (m)); } else if (op == 1 && br == 0) { TRACE_INSN (cpu, "I%i -= M%i;", i, m); dagsub (cpu, i, MREG (m)); } else illegal_instruction (cpu); } static void decode_dagMODik_0 (SIM_CPU *cpu, bu16 iw0) { /* dagMODik +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 |.op....|.i.....| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int i = ((iw0 >> DagMODik_i_bits) & DagMODik_i_mask); int op = ((iw0 >> DagMODik_op_bits) & DagMODik_op_mask); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_dagMODik); TRACE_EXTRACT (cpu, "%s: op:%i i:%i", __func__, op, i); if (op == 0) { TRACE_INSN (cpu, "I%i += 2;", i); dagadd (cpu, i, 2); } else if (op == 1) { TRACE_INSN (cpu, "I%i -= 2;", i); dagsub (cpu, i, 2); } else if (op == 2) { TRACE_INSN (cpu, "I%i += 4;", i); dagadd (cpu, i, 4); } else if (op == 3) { TRACE_INSN (cpu, "I%i -= 4;", i); dagsub (cpu, i, 4); } else illegal_instruction (cpu); } static void decode_dspLDST_0 (SIM_CPU *cpu, bu16 iw0) { /* dspLDST +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 0 | 0 | 1 | 1 | 1 |.W.|.aop...|.m.....|.i.....|.reg.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int i = ((iw0 >> DspLDST_i_bits) & DspLDST_i_mask); int m = ((iw0 >> DspLDST_m_bits) & DspLDST_m_mask); int W = ((iw0 >> DspLDST_W_bits) & DspLDST_W_mask); int aop = ((iw0 >> DspLDST_aop_bits) & DspLDST_aop_mask); int reg = ((iw0 >> DspLDST_reg_bits) & DspLDST_reg_mask); bu32 addr; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_dspLDST); TRACE_EXTRACT (cpu, "%s: aop:%i m:%i i:%i reg:%i", __func__, aop, m, i, reg); if (aop == 0 && W == 0 && m == 0) { TRACE_INSN (cpu, "R%i = [I%i++];", reg, i); addr = IREG (i); if (DIS_ALGN_EXPT & 0x1) addr &= ~3; dagadd (cpu, i, 4); STORE (DREG (reg), GET_LONG (addr)); } else if (aop == 0 && W == 0 && m == 1) { TRACE_INSN (cpu, "R%i.L = W[I%i++];", reg, i); addr = IREG (i); dagadd (cpu, i, 2); STORE (DREG (reg), (DREG (reg) & 0xFFFF0000) | GET_WORD (addr)); } else if (aop == 0 && W == 0 && m == 2) { TRACE_INSN (cpu, "R%i.H = W[I%i++];", reg, i); addr = IREG (i); dagadd (cpu, i, 2); STORE (DREG (reg), (DREG (reg) & 0xFFFF) | (GET_WORD (addr) << 16)); } else if (aop == 1 && W == 0 && m == 0) { TRACE_INSN (cpu, "R%i = [I%i--];", reg, i); addr = IREG (i); if (DIS_ALGN_EXPT & 0x1) addr &= ~3; dagsub (cpu, i, 4); STORE (DREG (reg), GET_LONG (addr)); } else if (aop == 1 && W == 0 && m == 1) { TRACE_INSN (cpu, "R%i.L = W[I%i--];", reg, i); addr = IREG (i); dagsub (cpu, i, 2); STORE (DREG (reg), (DREG (reg) & 0xFFFF0000) | GET_WORD (addr)); } else if (aop == 1 && W == 0 && m == 2) { TRACE_INSN (cpu, "R%i.H = W[I%i--];", reg, i); addr = IREG (i); dagsub (cpu, i, 2); STORE (DREG (reg), (DREG (reg) & 0xFFFF) | (GET_WORD (addr) << 16)); } else if (aop == 2 && W == 0 && m == 0) { TRACE_INSN (cpu, "R%i = [I%i];", reg, i); addr = IREG (i); if (DIS_ALGN_EXPT & 0x1) addr &= ~3; STORE (DREG (reg), GET_LONG (addr)); } else if (aop == 2 && W == 0 && m == 1) { TRACE_INSN (cpu, "R%i.L = W[I%i];", reg, i); addr = IREG (i); STORE (DREG (reg), (DREG (reg) & 0xFFFF0000) | GET_WORD (addr)); } else if (aop == 2 && W == 0 && m == 2) { TRACE_INSN (cpu, "R%i.H = W[I%i];", reg, i); addr = IREG (i); STORE (DREG (reg), (DREG (reg) & 0xFFFF) | (GET_WORD (addr) << 16)); } else if (aop == 0 && W == 1 && m == 0) { TRACE_INSN (cpu, "[I%i++] = R%i;", i, reg); addr = IREG (i); dagadd (cpu, i, 4); PUT_LONG (addr, DREG (reg)); } else if (aop == 0 && W == 1 && m == 1) { TRACE_INSN (cpu, "W[I%i++] = R%i.L;", i, reg); addr = IREG (i); dagadd (cpu, i, 2); PUT_WORD (addr, DREG (reg)); } else if (aop == 0 && W == 1 && m == 2) { TRACE_INSN (cpu, "W[I%i++] = R%i.H;", i, reg); addr = IREG (i); dagadd (cpu, i, 2); PUT_WORD (addr, DREG (reg) >> 16); } else if (aop == 1 && W == 1 && m == 0) { TRACE_INSN (cpu, "[I%i--] = R%i;", i, reg); addr = IREG (i); dagsub (cpu, i, 4); PUT_LONG (addr, DREG (reg)); } else if (aop == 1 && W == 1 && m == 1) { TRACE_INSN (cpu, "W[I%i--] = R%i.L;", i, reg); addr = IREG (i); dagsub (cpu, i, 2); PUT_WORD (addr, DREG (reg)); } else if (aop == 1 && W == 1 && m == 2) { TRACE_INSN (cpu, "W[I%i--] = R%i.H;", i, reg); addr = IREG (i); dagsub (cpu, i, 2); PUT_WORD (addr, DREG (reg) >> 16); } else if (aop == 2 && W == 1 && m == 0) { TRACE_INSN (cpu, "[I%i] = R%i;", i, reg); addr = IREG (i); PUT_LONG (addr, DREG (reg)); } else if (aop == 2 && W == 1 && m == 1) { TRACE_INSN (cpu, "W[I%i] = R%i.L;", i, reg); addr = IREG (i); PUT_WORD (addr, DREG (reg)); } else if (aop == 2 && W == 1 && m == 2) { TRACE_INSN (cpu, "W[I%i] = R%i.H;", i, reg); addr = IREG (i); PUT_WORD (addr, DREG (reg) >> 16); } else if (aop == 3 && W == 0) { TRACE_INSN (cpu, "R%i = [I%i ++ M%i];", reg, i, m); addr = IREG (i); if (DIS_ALGN_EXPT & 0x1) addr &= ~3; dagadd (cpu, i, MREG (m)); STORE (DREG (reg), GET_LONG (addr)); } else if (aop == 3 && W == 1) { TRACE_INSN (cpu, "[I%i ++ M%i] = R%i;", i, m, reg); addr = IREG (i); dagadd (cpu, i, MREG (m)); PUT_LONG (addr, DREG (reg)); } else illegal_instruction (cpu); } static void decode_LDST_0 (SIM_CPU *cpu, bu16 iw0) { /* LDST +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 0 | 0 | 1 |.sz....|.W.|.aop...|.Z.|.ptr.......|.reg.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int Z = ((iw0 >> LDST_Z_bits) & LDST_Z_mask); int W = ((iw0 >> LDST_W_bits) & LDST_W_mask); int sz = ((iw0 >> LDST_sz_bits) & LDST_sz_mask); int aop = ((iw0 >> LDST_aop_bits) & LDST_aop_mask); int reg = ((iw0 >> LDST_reg_bits) & LDST_reg_mask); int ptr = ((iw0 >> LDST_ptr_bits) & LDST_ptr_mask); const char * const posts[] = { "++", "--", "" }; const char *post = posts[aop]; const char *ptr_name = get_preg_name (ptr); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_LDST); TRACE_EXTRACT (cpu, "%s: sz:%i W:%i aop:%i Z:%i ptr:%i reg:%i", __func__, sz, W, aop, Z, ptr, reg); if (aop == 3) illegal_instruction (cpu); if (W == 0) { if (sz == 0 && Z == 0) { TRACE_INSN (cpu, "R%i = [%s%s];", reg, ptr_name, post); SET_DREG (reg, GET_LONG (PREG (ptr))); } else if (sz == 0 && Z == 1) { TRACE_INSN (cpu, "%s = [%s%s];", get_preg_name (reg), ptr_name, post); if (aop < 2 && ptr == reg) illegal_instruction_combination (cpu); SET_PREG (reg, GET_LONG (PREG (ptr))); } else if (sz == 1 && Z == 0) { TRACE_INSN (cpu, "R%i = W[%s%s] (Z);", reg, ptr_name, post); SET_DREG (reg, GET_WORD (PREG (ptr))); } else if (sz == 1 && Z == 1) { TRACE_INSN (cpu, "R%i = W[%s%s] (X);", reg, ptr_name, post); SET_DREG (reg, (bs32) (bs16) GET_WORD (PREG (ptr))); } else if (sz == 2 && Z == 0) { TRACE_INSN (cpu, "R%i = B[%s%s] (Z);", reg, ptr_name, post); SET_DREG (reg, GET_BYTE (PREG (ptr))); } else if (sz == 2 && Z == 1) { TRACE_INSN (cpu, "R%i = B[%s%s] (X);", reg, ptr_name, post); SET_DREG (reg, (bs32) (bs8) GET_BYTE (PREG (ptr))); } else illegal_instruction (cpu); } else { if (sz == 0 && Z == 0) { TRACE_INSN (cpu, "[%s%s] = R%i;", ptr_name, post, reg); PUT_LONG (PREG (ptr), DREG (reg)); } else if (sz == 0 && Z == 1) { TRACE_INSN (cpu, "[%s%s] = %s;", ptr_name, post, get_preg_name (reg)); PUT_LONG (PREG (ptr), PREG (reg)); } else if (sz == 1 && Z == 0) { TRACE_INSN (cpu, "W[%s%s] = R%i;", ptr_name, post, reg); PUT_WORD (PREG (ptr), DREG (reg)); } else if (sz == 2 && Z == 0) { TRACE_INSN (cpu, "B[%s%s] = R%i;", ptr_name, post, reg); PUT_BYTE (PREG (ptr), DREG (reg)); } else illegal_instruction (cpu); } if (aop == 0) SET_PREG (ptr, PREG (ptr) + (1 << (2 - sz))); if (aop == 1) SET_PREG (ptr, PREG (ptr) - (1 << (2 - sz))); } static void decode_LDSTiiFP_0 (SIM_CPU *cpu, bu16 iw0) { /* LDSTiiFP +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 0 | 1 | 1 | 1 | 0 |.W.|.offset............|.reg...........| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ /* This isn't exactly a grp:reg as this insn only supports Dregs & Pregs, but for our usage, its functionality the same thing. */ int grp = ((iw0 >> 3) & 0x1); int reg = ((iw0 >> LDSTiiFP_reg_bits) & 0x7 /*LDSTiiFP_reg_mask*/); int offset = ((iw0 >> LDSTiiFP_offset_bits) & LDSTiiFP_offset_mask); int W = ((iw0 >> LDSTiiFP_W_bits) & LDSTiiFP_W_mask); bu32 imm = negimm5s4 (offset); bu32 ea = FPREG + imm; const char *imm_str = negimm5s4_str (offset); const char *reg_name = get_allreg_name (grp, reg); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_LDSTiiFP); TRACE_EXTRACT (cpu, "%s: W:%i offset:%#x grp:%i reg:%i", __func__, W, offset, grp, reg); TRACE_DECODE (cpu, "%s: negimm5s4:%#x", __func__, imm); if (W == 0) { TRACE_INSN (cpu, "%s = [FP + %s];", reg_name, imm_str); reg_write (cpu, grp, reg, GET_LONG (ea)); } else { TRACE_INSN (cpu, "[FP + %s] = %s;", imm_str, reg_name); PUT_LONG (ea, reg_read (cpu, grp, reg)); } } static void decode_LDSTii_0 (SIM_CPU *cpu, bu16 iw0) { /* LDSTii +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 0 | 1 |.W.|.op....|.offset........|.ptr.......|.reg.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int reg = ((iw0 >> LDSTii_reg_bit) & LDSTii_reg_mask); int ptr = ((iw0 >> LDSTii_ptr_bit) & LDSTii_ptr_mask); int offset = ((iw0 >> LDSTii_offset_bit) & LDSTii_offset_mask); int op = ((iw0 >> LDSTii_op_bit) & LDSTii_op_mask); int W = ((iw0 >> LDSTii_W_bit) & LDSTii_W_mask); bu32 imm, ea; const char *imm_str; const char *ptr_name = get_preg_name (ptr); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_LDSTii); TRACE_EXTRACT (cpu, "%s: W:%i op:%i offset:%#x ptr:%i reg:%i", __func__, W, op, offset, ptr, reg); if (op == 0 || op == 3) imm = uimm4s4 (offset), imm_str = uimm4s4_str (offset); else imm = uimm4s2 (offset), imm_str = uimm4s2_str (offset); ea = PREG (ptr) + imm; TRACE_DECODE (cpu, "%s: uimm4s4/uimm4s2:%#x", __func__, imm); if (W == 1 && op == 2) illegal_instruction (cpu); if (W == 0) { if (op == 0) { TRACE_INSN (cpu, "R%i = [%s + %s];", reg, ptr_name, imm_str); SET_DREG (reg, GET_LONG (ea)); } else if (op == 1) { TRACE_INSN (cpu, "R%i = W[%s + %s] (Z);", reg, ptr_name, imm_str); SET_DREG (reg, GET_WORD (ea)); } else if (op == 2) { TRACE_INSN (cpu, "R%i = W[%s + %s] (X);", reg, ptr_name, imm_str); SET_DREG (reg, (bs32) (bs16) GET_WORD (ea)); } else if (op == 3) { TRACE_INSN (cpu, "%s = [%s + %s];", get_preg_name (reg), ptr_name, imm_str); SET_PREG (reg, GET_LONG (ea)); } } else { if (op == 0) { TRACE_INSN (cpu, "[%s + %s] = R%i;", ptr_name, imm_str, reg); PUT_LONG (ea, DREG (reg)); } else if (op == 1) { TRACE_INSN (cpu, "W[%s + %s] = R%i;", ptr_name, imm_str, reg); PUT_WORD (ea, DREG (reg)); } else if (op == 3) { TRACE_INSN (cpu, "[%s + %s] = %s;", ptr_name, imm_str, get_preg_name (reg)); PUT_LONG (ea, PREG (reg)); } } } static void decode_LoopSetup_0 (SIM_CPU *cpu, bu16 iw0, bu16 iw1, bu32 pc) { /* LoopSetup +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |.rop...|.c.|.soffset.......| |.reg...........| - | - |.eoffset...............................| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int c = ((iw0 >> (LoopSetup_c_bits - 16)) & LoopSetup_c_mask); int reg = ((iw1 >> LoopSetup_reg_bits) & LoopSetup_reg_mask); int rop = ((iw0 >> (LoopSetup_rop_bits - 16)) & LoopSetup_rop_mask); int soffset = ((iw0 >> (LoopSetup_soffset_bits - 16)) & LoopSetup_soffset_mask); int eoffset = ((iw1 >> LoopSetup_eoffset_bits) & LoopSetup_eoffset_mask); int spcrel = pcrel4 (soffset); int epcrel = lppcrel10 (eoffset); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_LoopSetup); TRACE_EXTRACT (cpu, "%s: rop:%i c:%i soffset:%i reg:%i eoffset:%i", __func__, rop, c, soffset, reg, eoffset); TRACE_DECODE (cpu, "%s: s_pcrel4:%#x e_lppcrel10:%#x", __func__, spcrel, epcrel); if (reg > 7) illegal_instruction (cpu); if (INSN_LEN == 8) illegal_instruction_combination (cpu); if (rop == 0) { TRACE_INSN (cpu, "LSETUP (%#x, %#x) LC%i;", spcrel, epcrel, c); } else if (rop == 1 && reg <= 7) { TRACE_INSN (cpu, "LSETUP (%#x, %#x) LC%i = %s;", spcrel, epcrel, c, get_preg_name (reg)); SET_LCREG (c, PREG (reg)); } else if (rop == 3 && reg <= 7) { TRACE_INSN (cpu, "LSETUP (%#x, %#x) LC%i = %s >> 1;", spcrel, epcrel, c, get_preg_name (reg)); SET_LCREG (c, PREG (reg) >> 1); } else illegal_instruction (cpu); SET_LTREG (c, pc + spcrel); SET_LBREG (c, pc + epcrel); } static void decode_LDIMMhalf_0 (SIM_CPU *cpu, bu16 iw0, bu16 iw1) { /* LDIMMhalf +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 1 |.Z.|.H.|.S.|.grp...|.reg.......| |.hword.........................................................| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int H = ((iw0 >> (LDIMMhalf_H_bits - 16)) & LDIMMhalf_H_mask); int Z = ((iw0 >> (LDIMMhalf_Z_bits - 16)) & LDIMMhalf_Z_mask); int S = ((iw0 >> (LDIMMhalf_S_bits - 16)) & LDIMMhalf_S_mask); int reg = ((iw0 >> (LDIMMhalf_reg_bits - 16)) & LDIMMhalf_reg_mask); int grp = ((iw0 >> (LDIMMhalf_grp_bits - 16)) & LDIMMhalf_grp_mask); int hword = ((iw1 >> LDIMMhalf_hword_bits) & LDIMMhalf_hword_mask); bu32 val; const char *val_str; const char *reg_name = get_allreg_name (grp, reg); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_LDIMMhalf); TRACE_EXTRACT (cpu, "%s: Z:%i H:%i S:%i grp:%i reg:%i hword:%#x", __func__, Z, H, S, grp, reg, hword); if (INSN_LEN == 8) illegal_instruction_combination (cpu); if (S == 1) val = imm16 (hword), val_str = imm16_str (hword); else val = luimm16 (hword), val_str = luimm16_str (hword); if (H == 0 && S == 1 && Z == 0) { TRACE_INSN (cpu, "%s = %s (X);", reg_name, val_str); } else if (H == 0 && S == 0 && Z == 1) { TRACE_INSN (cpu, "%s = %s (Z);", reg_name, val_str); } else if (H == 0 && S == 0 && Z == 0) { TRACE_INSN (cpu, "%s.L = %s;", reg_name, val_str); val = REG_H_L (reg_read (cpu, grp, reg), val); } else if (H == 1 && S == 0 && Z == 0) { TRACE_INSN (cpu, "%s.H = %s;", reg_name, val_str); val = REG_H_L (val << 16, reg_read (cpu, grp, reg)); } else illegal_instruction (cpu); reg_write (cpu, grp, reg, val); } static void decode_CALLa_0 (SIM_CPU *cpu, bu16 iw0, bu16 iw1, bu32 pc) { /* CALLa +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 1 | 0 | 0 | 0 | 1 |.S.|.msw...........................| |.lsw...........................................................| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int S = ((iw0 >> (CALLa_S_bits - 16)) & CALLa_S_mask); int lsw = ((iw1 >> 0) & 0xffff); int msw = ((iw0 >> 0) & 0xff); int pcrel = pcrel24 ((msw << 16) | lsw); bu32 newpc = pc + pcrel; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_CALLa); TRACE_EXTRACT (cpu, "%s: S:%i msw:%#x lsw:%#x", __func__, S, msw, lsw); TRACE_DECODE (cpu, "%s: pcrel24:%#x", __func__, pcrel); TRACE_INSN (cpu, "%s %#x;", S ? "CALL" : "JUMP.L", pcrel); if (INSN_LEN == 8) illegal_instruction_combination (cpu); if (S == 1) { TRACE_BRANCH (cpu, pc, newpc, -1, "CALL"); SET_RETSREG (hwloop_get_next_pc (cpu, pc, 4)); } else TRACE_BRANCH (cpu, pc, newpc, -1, "JUMP.L"); SET_PCREG (newpc); BFIN_CPU_STATE.did_jump = true; PROFILE_BRANCH_TAKEN (cpu); CYCLE_DELAY = 5; } static void decode_LDSTidxI_0 (SIM_CPU *cpu, bu16 iw0, bu16 iw1) { /* LDSTidxI +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 1 | 0 | 0 | 1 |.W.|.Z.|.sz....|.ptr.......|.reg.......| |.offset........................................................| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int Z = ((iw0 >> (LDSTidxI_Z_bits - 16)) & LDSTidxI_Z_mask); int W = ((iw0 >> (LDSTidxI_W_bits - 16)) & LDSTidxI_W_mask); int sz = ((iw0 >> (LDSTidxI_sz_bits - 16)) & LDSTidxI_sz_mask); int reg = ((iw0 >> (LDSTidxI_reg_bits - 16)) & LDSTidxI_reg_mask); int ptr = ((iw0 >> (LDSTidxI_ptr_bits - 16)) & LDSTidxI_ptr_mask); int offset = ((iw1 >> LDSTidxI_offset_bits) & LDSTidxI_offset_mask); const char *ptr_name = get_preg_name (ptr); bu32 imm_16s4 = imm16s4 (offset); bu32 imm_16s2 = imm16s2 (offset); bu32 imm_16 = imm16 (offset); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_LDSTidxI); TRACE_EXTRACT (cpu, "%s: W:%i Z:%i sz:%i ptr:%i reg:%i offset:%#x", __func__, W, Z, sz, ptr, reg, offset); if (sz == 3) illegal_instruction (cpu); if (W == 0) { if (sz == 0 && Z == 0) { TRACE_INSN (cpu, "R%i = [%s + %s];", reg, ptr_name, imm16s4_str (offset)); SET_DREG (reg, GET_LONG (PREG (ptr) + imm_16s4)); } else if (sz == 0 && Z == 1) { TRACE_INSN (cpu, "%s = [%s + %s];", get_preg_name (reg), ptr_name, imm16s4_str (offset)); SET_PREG (reg, GET_LONG (PREG (ptr) + imm_16s4)); } else if (sz == 1 && Z == 0) { TRACE_INSN (cpu, "R%i = W[%s + %s] (Z);", reg, ptr_name, imm16s2_str (offset)); SET_DREG (reg, GET_WORD (PREG (ptr) + imm_16s2)); } else if (sz == 1 && Z == 1) { TRACE_INSN (cpu, "R%i = W[%s + %s] (X);", reg, ptr_name, imm16s2_str (offset)); SET_DREG (reg, (bs32) (bs16) GET_WORD (PREG (ptr) + imm_16s2)); } else if (sz == 2 && Z == 0) { TRACE_INSN (cpu, "R%i = B[%s + %s] (Z);", reg, ptr_name, imm16_str (offset)); SET_DREG (reg, GET_BYTE (PREG (ptr) + imm_16)); } else if (sz == 2 && Z == 1) { TRACE_INSN (cpu, "R%i = B[%s + %s] (X);", reg, ptr_name, imm16_str (offset)); SET_DREG (reg, (bs32) (bs8) GET_BYTE (PREG (ptr) + imm_16)); } } else { if (sz != 0 && Z != 0) illegal_instruction (cpu); if (sz == 0 && Z == 0) { TRACE_INSN (cpu, "[%s + %s] = R%i;", ptr_name, imm16s4_str (offset), reg); PUT_LONG (PREG (ptr) + imm_16s4, DREG (reg)); } else if (sz == 0 && Z == 1) { TRACE_INSN (cpu, "[%s + %s] = %s;", ptr_name, imm16s4_str (offset), get_preg_name (reg)); PUT_LONG (PREG (ptr) + imm_16s4, PREG (reg)); } else if (sz == 1 && Z == 0) { TRACE_INSN (cpu, "W[%s + %s] = R%i;", ptr_name, imm16s2_str (offset), reg); PUT_WORD (PREG (ptr) + imm_16s2, DREG (reg)); } else if (sz == 2 && Z == 0) { TRACE_INSN (cpu, "B[%s + %s] = R%i;", ptr_name, imm16_str (offset), reg); PUT_BYTE (PREG (ptr) + imm_16, DREG (reg)); } } } static void decode_linkage_0 (SIM_CPU *cpu, bu16 iw0, bu16 iw1) { /* linkage +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |.R.| |.framesize.....................................................| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int R = ((iw0 >> (Linkage_R_bits - 16)) & Linkage_R_mask); int framesize = ((iw1 >> Linkage_framesize_bits) & Linkage_framesize_mask); bu32 sp; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_linkage); TRACE_EXTRACT (cpu, "%s: R:%i framesize:%#x", __func__, R, framesize); if (R == 0) { int size = uimm16s4 (framesize); sp = SPREG; TRACE_INSN (cpu, "LINK %s;", uimm16s4_str (framesize)); if (INSN_LEN == 8) illegal_instruction_combination (cpu); sp -= 4; PUT_LONG (sp, RETSREG); sp -= 4; PUT_LONG (sp, FPREG); SET_FPREG (sp); sp -= size; CYCLE_DELAY = 3; } else { /* Restore SP from FP. */ sp = FPREG; TRACE_INSN (cpu, "UNLINK;"); if (INSN_LEN == 8) illegal_instruction_combination (cpu); SET_FPREG (GET_LONG (sp)); sp += 4; SET_RETSREG (GET_LONG (sp)); sp += 4; CYCLE_DELAY = 2; } SET_SPREG (sp); } static void decode_dsp32mac_0 (SIM_CPU *cpu, bu16 iw0, bu16 iw1) { /* dsp32mac +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 0 | 0 |.M.| 0 | 0 |.mmod..........|.MM|.P.|.w1|.op1...| |.h01|.h11|.w0|.op0...|.h00|.h10|.dst.......|.src0......|.src1..| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int op1 = ((iw0 >> (DSP32Mac_op1_bits - 16)) & DSP32Mac_op1_mask); int w1 = ((iw0 >> (DSP32Mac_w1_bits - 16)) & DSP32Mac_w1_mask); int P = ((iw0 >> (DSP32Mac_p_bits - 16)) & DSP32Mac_p_mask); int MM = ((iw0 >> (DSP32Mac_MM_bits - 16)) & DSP32Mac_MM_mask); int mmod = ((iw0 >> (DSP32Mac_mmod_bits - 16)) & DSP32Mac_mmod_mask); int M = ((iw0 >> (DSP32Mac_M_bits - 16)) & DSP32Mac_M_mask); int w0 = ((iw1 >> DSP32Mac_w0_bits) & DSP32Mac_w0_mask); int src0 = ((iw1 >> DSP32Mac_src0_bits) & DSP32Mac_src0_mask); int src1 = ((iw1 >> DSP32Mac_src1_bits) & DSP32Mac_src1_mask); int dst = ((iw1 >> DSP32Mac_dst_bits) & DSP32Mac_dst_mask); int h10 = ((iw1 >> DSP32Mac_h10_bits) & DSP32Mac_h10_mask); int h00 = ((iw1 >> DSP32Mac_h00_bits) & DSP32Mac_h00_mask); int op0 = ((iw1 >> DSP32Mac_op0_bits) & DSP32Mac_op0_mask); int h11 = ((iw1 >> DSP32Mac_h11_bits) & DSP32Mac_h11_mask); int h01 = ((iw1 >> DSP32Mac_h01_bits) & DSP32Mac_h01_mask); bu32 res = DREG (dst); bu32 v_i = 0, zero = 0; static const char * const ops[] = { "=", "+=", "-=" }; char _buf[128], *buf = _buf; int _MM = MM; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_dsp32mac); TRACE_EXTRACT (cpu, "%s: M:%i mmod:%i MM:%i P:%i w1:%i op1:%i h01:%i h11:%i " "w0:%i op0:%i h00:%i h10:%i dst:%i src0:%i src1:%i", __func__, M, mmod, MM, P, w1, op1, h01, h11, w0, op0, h00, h10, dst, src0, src1); if (w0 == 0 && w1 == 0 && op1 == 3 && op0 == 3) illegal_instruction (cpu); if ((w1 || w0) && mmod == M_W32) illegal_instruction (cpu); if (((1 << mmod) & (P ? 0x131b : 0x1b5f)) == 0) illegal_instruction (cpu); /* First handle MAC1 side. */ if (w1 == 1 || op1 != 3) { bu32 res1 = decode_macfunc (cpu, 1, op1, h01, h11, src0, src1, mmod, MM, P, &v_i); if (w1) buf += sprintf (buf, P ? "R%i" : "R%i.H", dst + P); if (op1 == 3) { buf += sprintf (buf, " = A1"); zero = !!(res1 == 0); } else { if (w1) buf += sprintf (buf, " = ("); buf += sprintf (buf, "A1 %s R%i.%c * R%i.%c", ops[op1], src0, h01 ? 'H' : 'L', src1, h11 ? 'H' : 'L'); if (w1) buf += sprintf (buf, ")"); } if (w1) { if (P) STORE (DREG (dst + 1), res1); else { if (res1 & 0xffff0000) illegal_instruction (cpu); res = REG_H_L (res1 << 16, res); } } if (w0 == 1 || op0 != 3) { if (_MM) buf += sprintf (buf, " (M)"); _MM = 0; buf += sprintf (buf, ", "); } } /* Then handle MAC0 side. */ if (w0 == 1 || op0 != 3) { bu32 res0 = decode_macfunc (cpu, 0, op0, h00, h10, src0, src1, mmod, 0, P, &v_i); if (w0) buf += sprintf (buf, P ? "R%i" : "R%i.L", dst); if (op0 == 3) { buf += sprintf (buf, " = A0"); zero |= !!(res0 == 0); } else { if (w0) buf += sprintf (buf, " = ("); buf += sprintf (buf, "A0 %s R%i.%c * R%i.%c", ops[op0], src0, h00 ? 'H' : 'L', src1, h10 ? 'H' : 'L'); if (w0) buf += sprintf (buf, ")"); } if (w0) { if (P) STORE (DREG (dst), res0); else { if (res0 & 0xffff0000) illegal_instruction (cpu); res = REG_H_L (res, res0); } } } TRACE_INSN (cpu, "%s%s;", _buf, mac_optmode (mmod, _MM)); if (!P && (w0 || w1)) { STORE (DREG (dst), res); SET_ASTATREG (v, v_i); if (v_i) SET_ASTATREG (vs, v_i); } else if (P) { SET_ASTATREG (v, v_i); if (v_i) SET_ASTATREG (vs, v_i); } if (op0 == 3 || op1 == 3) SET_ASTATREG (az, zero); } static void decode_dsp32mult_0 (SIM_CPU *cpu, bu16 iw0, bu16 iw1) { /* dsp32mult +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 0 | 0 |.M.| 0 | 1 |.mmod..........|.MM|.P.|.w1|.op1...| |.h01|.h11|.w0|.op0...|.h00|.h10|.dst.......|.src0......|.src1..| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int op1 = ((iw0 >> (DSP32Mac_op1_bits - 16)) & DSP32Mac_op1_mask); int w1 = ((iw0 >> (DSP32Mac_w1_bits - 16)) & DSP32Mac_w1_mask); int P = ((iw0 >> (DSP32Mac_p_bits - 16)) & DSP32Mac_p_mask); int MM = ((iw0 >> (DSP32Mac_MM_bits - 16)) & DSP32Mac_MM_mask); int mmod = ((iw0 >> (DSP32Mac_mmod_bits - 16)) & DSP32Mac_mmod_mask); int M = ((iw0 >> (DSP32Mac_M_bits - 16)) & DSP32Mac_M_mask); int w0 = ((iw1 >> DSP32Mac_w0_bits) & DSP32Mac_w0_mask); int src0 = ((iw1 >> DSP32Mac_src0_bits) & DSP32Mac_src0_mask); int src1 = ((iw1 >> DSP32Mac_src1_bits) & DSP32Mac_src1_mask); int dst = ((iw1 >> DSP32Mac_dst_bits) & DSP32Mac_dst_mask); int h10 = ((iw1 >> DSP32Mac_h10_bits) & DSP32Mac_h10_mask); int h00 = ((iw1 >> DSP32Mac_h00_bits) & DSP32Mac_h00_mask); int op0 = ((iw1 >> DSP32Mac_op0_bits) & DSP32Mac_op0_mask); int h11 = ((iw1 >> DSP32Mac_h11_bits) & DSP32Mac_h11_mask); int h01 = ((iw1 >> DSP32Mac_h01_bits) & DSP32Mac_h01_mask); bu32 res = DREG (dst); bu32 sat0 = 0, sat1 = 0; char _buf[128], *buf = _buf; int _MM = MM; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_dsp32mult); TRACE_EXTRACT (cpu, "%s: M:%i mmod:%i MM:%i P:%i w1:%i op1:%i h01:%i h11:%i " "w0:%i op0:%i h00:%i h10:%i dst:%i src0:%i src1:%i", __func__, M, mmod, MM, P, w1, op1, h01, h11, w0, op0, h00, h10, dst, src0, src1); if (w1 == 0 && w0 == 0) illegal_instruction (cpu); if (((1 << mmod) & (P ? 0x313 : 0x1b57)) == 0) illegal_instruction (cpu); if (P && ((dst & 1) || (op1 != 0) || (op0 != 0) || !is_macmod_pmove (mmod))) illegal_instruction (cpu); if (!P && ((op1 != 0) || (op0 != 0) || !is_macmod_hmove (mmod))) illegal_instruction (cpu); /* First handle MAC1 side. */ if (w1) { bu64 r = decode_multfunc (cpu, h01, h11, src0, src1, mmod, MM, &sat1); bu32 res1 = extract_mult (cpu, r, mmod, MM, P, NULL); buf += sprintf (buf, P ? "R%i" : "R%i.H", dst + P); buf += sprintf (buf, " = R%i.%c * R%i.%c", src0, h01 ? 'H' : 'L', src1, h11 ? 'H' : 'L'); if (w0) { if (_MM) buf += sprintf (buf, " (M)"); _MM = 0; buf += sprintf (buf, ", "); } if (P) STORE (DREG (dst + 1), res1); else { if (res1 & 0xFFFF0000) illegal_instruction (cpu); res = REG_H_L (res1 << 16, res); } } /* First handle MAC0 side. */ if (w0) { bu64 r = decode_multfunc (cpu, h00, h10, src0, src1, mmod, 0, &sat0); bu32 res0 = extract_mult (cpu, r, mmod, 0, P, NULL); buf += sprintf (buf, P ? "R%i" : "R%i.L", dst); buf += sprintf (buf, " = R%i.%c * R%i.%c", src0, h01 ? 'H' : 'L', src1, h11 ? 'H' : 'L'); if (P) STORE (DREG (dst), res0); else { if (res0 & 0xFFFF0000) illegal_instruction (cpu); res = REG_H_L (res, res0); } } TRACE_INSN (cpu, "%s%s;", _buf, mac_optmode (mmod, _MM)); if (!P && (w0 || w1)) STORE (DREG (dst), res); if (w0 || w1) { STORE (ASTATREG (v), sat0 | sat1); STORE (ASTATREG (v_copy), sat0 | sat1); if (sat0 | sat1) STORE (ASTATREG (vs), 1); } } static void decode_dsp32alu_0 (SIM_CPU *cpu, bu16 iw0, bu16 iw1) { /* dsp32alu +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 0 | 0 |.M.| 1 | 0 | - | - | - |.HL|.aopcde............| |.aop...|.s.|.x.|.dst0......|.dst1......|.src0......|.src1......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int s = ((iw1 >> DSP32Alu_s_bits) & DSP32Alu_s_mask); int x = ((iw1 >> DSP32Alu_x_bits) & DSP32Alu_x_mask); int aop = ((iw1 >> DSP32Alu_aop_bits) & DSP32Alu_aop_mask); int src0 = ((iw1 >> DSP32Alu_src0_bits) & DSP32Alu_src0_mask); int src1 = ((iw1 >> DSP32Alu_src1_bits) & DSP32Alu_src1_mask); int dst0 = ((iw1 >> DSP32Alu_dst0_bits) & DSP32Alu_dst0_mask); int dst1 = ((iw1 >> DSP32Alu_dst1_bits) & DSP32Alu_dst1_mask); int M = ((iw0 >> (DSP32Alu_M_bits - 16)) & DSP32Alu_M_mask); int HL = ((iw0 >> (DSP32Alu_HL_bits - 16)) & DSP32Alu_HL_mask); int aopcde = ((iw0 >> (DSP32Alu_aopcde_bits - 16)) & DSP32Alu_aopcde_mask); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_dsp32alu); TRACE_EXTRACT (cpu, "%s: M:%i HL:%i aopcde:%i aop:%i s:%i x:%i dst0:%i " "dst1:%i src0:%i src1:%i", __func__, M, HL, aopcde, aop, s, x, dst0, dst1, src0, src1); if ((aop == 0 || aop == 2) && aopcde == 9 && HL == 0 && s == 0) { int a = aop >> 1; TRACE_INSN (cpu, "A%i.L = R%i.L;", a, src0); SET_AWREG (a, REG_H_L (AWREG (a), DREG (src0))); } else if ((aop == 0 || aop == 2) && aopcde == 9 && HL == 1 && s == 0) { int a = aop >> 1; TRACE_INSN (cpu, "A%i.H = R%i.H;", a, src0); SET_AWREG (a, REG_H_L (DREG (src0), AWREG (a))); } else if ((aop == 1 || aop == 0) && aopcde == 5) { bs32 val0 = DREG (src0); bs32 val1 = DREG (src1); bs32 res; bs32 signRes; bs32 ovX, sBit1, sBit2, sBitRes1, sBitRes2; TRACE_INSN (cpu, "R%i.%s = R%i %s R%i (RND12)", dst0, HL ? "L" : "H", src0, aop & 0x1 ? "-" : "+", src1); /* If subtract, just invert and add one. */ if (aop & 0x1) val1 = ~val1 + 1; /* Get the sign bits, since we need them later. */ sBit1 = !!(val0 & 0x80000000); sBit2 = !!(val1 & 0x80000000); res = val0 + val1; sBitRes1 = !!(res & 0x80000000); /* Round to the 12th bit. */ res += 0x0800; sBitRes2 = !!(res & 0x80000000); signRes = res; signRes >>= 27; /* Overflow if pos + pos = neg neg + neg = pos positive_res + positive_round = neg Shift and upper 4 bits where not the same. */ if ((!(sBit1 ^ sBit2) && (sBit1 ^ sBitRes1)) || (!sBit1 && !sBit2 && sBitRes2) || ((signRes != 0) && (signRes != -1))) { /* Both X1 and X2 Neg res is neg overflow. */ if (sBit1 && sBit2) res = 0x80000000; /* Both X1 and X2 Pos res is pos overflow. */ else if (!sBit1 && !sBit2) res = 0x7FFFFFFF; /* Pos+Neg or Neg+Pos take the sign of the result. */ else if (sBitRes1) res = 0x80000000; else res = 0x7FFFFFFF; ovX = 1; } else { /* Shift up now after overflow detection. */ ovX = 0; res <<= 4; } res >>= 16; if (HL) STORE (DREG (dst0), REG_H_L (res << 16, DREG (dst0))); else STORE (DREG (dst0), REG_H_L (DREG (dst0), res)); SET_ASTATREG (az, res == 0); SET_ASTATREG (an, res & 0x8000); SET_ASTATREG (v, ovX); if (ovX) SET_ASTATREG (vs, ovX); } else if ((aop == 2 || aop == 3) && aopcde == 5) { bs32 val0 = DREG (src0); bs32 val1 = DREG (src1); bs32 res; TRACE_INSN (cpu, "R%i.%s = R%i %s R%i (RND20)", dst0, HL ? "L" : "H", src0, aop & 0x1 ? "-" : "+", src1); /* If subtract, just invert and add one. */ if (aop & 0x1) val1 = ~val1 + 1; res = (val0 >> 4) + (val1 >> 4) + (((val0 & 0xf) + (val1 & 0xf)) >> 4); res += 0x8000; /* Don't sign extend during the shift. */ res = ((bu32)res >> 16); /* Don't worry about overflows, since we are shifting right. */ if (HL) STORE (DREG (dst0), REG_H_L (res << 16, DREG (dst0))); else STORE (DREG (dst0), REG_H_L (DREG (dst0), res)); SET_ASTATREG (az, res == 0); SET_ASTATREG (an, res & 0x8000); SET_ASTATREG (v, 0); } else if (aopcde == 2 || aopcde == 3) { bu32 s1, s2, val, ac0_i = 0, v_i = 0; TRACE_INSN (cpu, "R%i.%c = R%i.%c %c R%i.%c%s;", dst0, HL ? 'H' : 'L', src0, aop & 2 ? 'H' : 'L', aopcde == 2 ? '+' : '-', src1, aop & 1 ? 'H' : 'L', amod1 (s, x)); s1 = DREG (src0); s2 = DREG (src1); if (aop & 1) s2 >>= 16; if (aop & 2) s1 >>= 16; if (aopcde == 2) val = add16 (cpu, s1, s2, &ac0_i, &v_i, 0, 0, s, 0); else val = sub16 (cpu, s1, s2, &ac0_i, &v_i, 0, 0, s, 0); SET_ASTATREG (ac0, ac0_i); SET_ASTATREG (v, v_i); if (HL) SET_DREG_H (dst0, val << 16); else SET_DREG_L (dst0, val); SET_ASTATREG (an, val & 0x8000); } else if ((aop == 0 || aop == 2) && aopcde == 9 && s == 1) { int a = aop >> 1; TRACE_INSN (cpu, "A%i = R%i;", a, src0); SET_AREG32 (a, DREG (src0)); } else if ((aop == 1 || aop == 3) && aopcde == 9 && s == 0) { int a = aop >> 1; TRACE_INSN (cpu, "A%i.X = R%i.L;", a, src0); SET_AXREG (a, (bs8)DREG (src0)); } else if (aop == 3 && aopcde == 11 && (s == 0 || s == 1)) { bu64 acc0 = get_extended_acc (cpu, 0); bu64 acc1 = get_extended_acc (cpu, 1); bu32 carry = (bu40)acc1 < (bu40)acc0; bu32 sat = 0; TRACE_INSN (cpu, "A0 -= A1%s;", s ? " (W32)" : ""); acc0 -= acc1; if ((bs64)acc0 < -0x8000000000ll) acc0 = -0x8000000000ull, sat = 1; else if ((bs64)acc0 >= 0x7fffffffffll) acc0 = 0x7fffffffffull, sat = 1; if (s == 1) { /* A0 -= A1 (W32) */ if (acc0 & (bu64)0x8000000000ll) acc0 &= 0x80ffffffffll, sat = 1; else acc0 &= 0xffffffffll; } STORE (AXREG (0), (acc0 >> 32) & 0xff); STORE (AWREG (0), acc0 & 0xffffffff); STORE (ASTATREG (az), acc0 == 0); STORE (ASTATREG (an), !!(acc0 & (bu64)0x8000000000ll)); STORE (ASTATREG (ac0), carry); STORE (ASTATREG (ac0_copy), carry); STORE (ASTATREG (av0), sat); if (sat) STORE (ASTATREG (av0s), sat); } else if ((aop == 0 || aop == 1) && aopcde == 22) { bu32 s0, s0L, s0H, s1, s1L, s1H; bu32 tmp0, tmp1, i; const char * const opts[] = { "rndl", "rndh", "tl", "th" }; TRACE_INSN (cpu, "R%i = BYTEOP2P (R%i:%i, R%i:%i) (%s%s);", dst0, src0 + 1, src0, src1 + 1, src1, opts[HL + (aop << 1)], s ? ", r" : ""); if (src0 == src1) illegal_instruction_combination (cpu); s0L = DREG (src0); s0H = DREG (src0 + 1); s1L = DREG (src1); s1H = DREG (src1 + 1); if (s) { s0 = algn (s0H, s0L, IREG (0) & 3); s1 = algn (s1H, s1L, IREG (0) & 3); } else { s0 = algn (s0L, s0H, IREG (0) & 3); s1 = algn (s1L, s1H, IREG (0) & 3); } i = !aop * 2; tmp0 = ((((s1 >> 8) & 0xff) + ((s1 >> 0) & 0xff) + ((s0 >> 8) & 0xff) + ((s0 >> 0) & 0xff) + i) >> 2) & 0xff; tmp1 = ((((s1 >> 24) & 0xff) + ((s1 >> 16) & 0xff) + ((s0 >> 24) & 0xff) + ((s0 >> 16) & 0xff) + i) >> 2) & 0xff; SET_DREG (dst0, (tmp1 << (16 + (HL * 8))) | (tmp0 << (HL * 8))); } else if ((aop == 0 || aop == 1) && s == 0 && aopcde == 8) { TRACE_INSN (cpu, "A%i = 0;", aop); SET_AREG (aop, 0); } else if (aop == 2 && s == 0 && aopcde == 8) { TRACE_INSN (cpu, "A1 = A0 = 0;"); SET_AREG (0, 0); SET_AREG (1, 0); } else if ((aop == 0 || aop == 1 || aop == 2) && s == 1 && aopcde == 8) { bs40 acc0 = get_extended_acc (cpu, 0); bs40 acc1 = get_extended_acc (cpu, 1); bu32 sat; if (aop == 0 || aop == 1) TRACE_INSN (cpu, "A%i = A%i (S);", aop, aop); else TRACE_INSN (cpu, "A1 = A1 (S), A0 = A0 (S);"); if (aop == 0 || aop == 2) { sat = 0; acc0 = saturate_s32 (acc0, &sat); acc0 |= -(acc0 & 0x80000000ull); SET_AXREG (0, (acc0 >> 31) & 0xFF); SET_AWREG (0, acc0 & 0xFFFFFFFF); SET_ASTATREG (av0, sat); if (sat) SET_ASTATREG (av0s, sat); } else acc0 = 1; if (aop == 1 || aop == 2) { sat = 0; acc1 = saturate_s32 (acc1, &sat); acc1 |= -(acc1 & 0x80000000ull); SET_AXREG (1, (acc1 >> 31) & 0xFF); SET_AWREG (1, acc1 & 0xFFFFFFFF); SET_ASTATREG (av1, sat); if (sat) SET_ASTATREG (av1s, sat); } else acc1 = 1; SET_ASTATREG (az, (acc0 == 0) || (acc1 == 0)); SET_ASTATREG (an, ((acc0 >> 31) & 1) || ((acc1 >> 31) & 1)); } else if (aop == 3 && (s == 0 || s == 1) && aopcde == 8) { TRACE_INSN (cpu, "A%i = A%i;", s, !s); SET_AXREG (s, AXREG (!s)); SET_AWREG (s, AWREG (!s)); } else if (aop == 3 && HL == 0 && aopcde == 16) { int i; bu32 az; TRACE_INSN (cpu, "A1 = ABS A1 , A0 = ABS A0;"); az = 0; for (i = 0; i < 2; ++i) { bu32 av; bs40 acc = get_extended_acc (cpu, i); if (acc >> 39) acc = -acc; av = acc == ((bs40)1 << 39); if (av) acc = ((bs40)1 << 39) - 1; SET_AREG (i, acc); SET_ASTATREG (av[i], av); if (av) SET_ASTATREG (avs[i], av); az |= (acc == 0); } SET_ASTATREG (az, az); SET_ASTATREG (an, 0); } else if (aop == 0 && aopcde == 23) { bu32 s0, s0L, s0H, s1, s1L, s1H; bs32 tmp0, tmp1; TRACE_INSN (cpu, "R%i = BYTEOP3P (R%i:%i, R%i:%i) (%s%s);", dst0, src0 + 1, src0, src1 + 1, src1, HL ? "HI" : "LO", s ? ", R" : ""); if (src0 == src1) illegal_instruction_combination (cpu); s0L = DREG (src0); s0H = DREG (src0 + 1); s1L = DREG (src1); s1H = DREG (src1 + 1); if (s) { s0 = algn (s0H, s0L, IREG (0) & 3); s1 = algn (s1H, s1L, IREG (1) & 3); } else { s0 = algn (s0L, s0H, IREG (0) & 3); s1 = algn (s1L, s1H, IREG (1) & 3); } tmp0 = (bs32)(bs16)(s0 >> 0) + ((s1 >> ( 0 + (8 * !HL))) & 0xff); tmp1 = (bs32)(bs16)(s0 >> 16) + ((s1 >> (16 + (8 * !HL))) & 0xff); SET_DREG (dst0, (CLAMP (tmp0, 0, 255) << ( 0 + (8 * HL))) | (CLAMP (tmp1, 0, 255) << (16 + (8 * HL)))); } else if ((aop == 0 || aop == 1) && aopcde == 16) { bu32 av; bs40 acc; TRACE_INSN (cpu, "A%i = ABS A%i;", HL, aop); acc = get_extended_acc (cpu, aop); if (acc >> 39) acc = -acc; av = acc == ((bs40)1 << 39); if (av) acc = ((bs40)1 << 39) - 1; SET_AREG (HL, acc); SET_ASTATREG (av[HL], av); if (av) SET_ASTATREG (avs[HL], av); SET_ASTATREG (az, acc == 0); SET_ASTATREG (an, 0); } else if (aop == 3 && aopcde == 12) { bs32 res = DREG (src0); bs32 ovX; bool sBit_a, sBit_b; TRACE_INSN (cpu, "R%i.%s = R%i (RND);", dst0, HL == 0 ? "L" : "H", src0); TRACE_DECODE (cpu, "R%i.%s = R%i:%#x (RND);", dst0, HL == 0 ? "L" : "H", src0, res); sBit_b = !!(res & 0x80000000); res += 0x8000; sBit_a = !!(res & 0x80000000); /* Overflow if the sign bit changed when we rounded. */ if ((res >> 16) && (sBit_b != sBit_a)) { ovX = 1; if (!sBit_b) res = 0x7FFF; else res = 0x8000; } else { res = res >> 16; ovX = 0; } if (!HL) SET_DREG (dst0, REG_H_L (DREG (dst0), res)); else SET_DREG (dst0, REG_H_L (res << 16, DREG (dst0))); SET_ASTATREG (az, res == 0); SET_ASTATREG (an, res < 0); SET_ASTATREG (v, ovX); if (ovX) SET_ASTATREG (vs, ovX); } else if (aop == 3 && HL == 0 && aopcde == 15) { bu32 hi = (-(bs16)(DREG (src0) >> 16)) << 16; bu32 lo = (-(bs16)(DREG (src0) & 0xFFFF)) & 0xFFFF; int v, ac0, ac1; TRACE_INSN (cpu, "R%i = -R%i (V);", dst0, src0); v = ac0 = ac1 = 0; if (hi == 0x80000000) { hi = 0x7fff0000; v = 1; } else if (hi == 0) ac1 = 1; if (lo == 0x8000) { lo = 0x7fff; v = 1; } else if (lo == 0) ac0 = 1; SET_DREG (dst0, hi | lo); SET_ASTATREG (v, v); if (v) SET_ASTATREG (vs, 1); SET_ASTATREG (ac0, ac0); SET_ASTATREG (ac1, ac1); setflags_nz_2x16 (cpu, DREG (dst0)); } else if (aop == 3 && HL == 0 && aopcde == 14) { TRACE_INSN (cpu, "A1 = - A1 , A0 = - A0;"); SET_AREG (0, saturate_s40 (-get_extended_acc (cpu, 0))); SET_AREG (1, saturate_s40 (-get_extended_acc (cpu, 1))); /* XXX: what ASTAT flags need updating ? */ } else if ((aop == 0 || aop == 1) && (HL == 0 || HL == 1) && aopcde == 14) { bs40 src_acc = get_extended_acc (cpu, aop); TRACE_INSN (cpu, "A%i = - A%i;", HL, aop); SET_AREG (HL, saturate_s40 (-src_acc)); SET_ASTATREG (az, AWREG (HL) == 0 && AXREG (HL) == 0); SET_ASTATREG (an, AXREG (HL) >> 7); SET_ASTATREG (ac0, src_acc == 0); if (HL == 0) { SET_ASTATREG (av0, src_acc < 0); if (ASTATREG (av0)) SET_ASTATREG (av0s, 1); } else { SET_ASTATREG (av1, src_acc < 0); if (ASTATREG (av1)) SET_ASTATREG (av1s, 1); } } else if (aop == 0 && aopcde == 12) { bs16 tmp0_hi = DREG (src0) >> 16; bs16 tmp0_lo = DREG (src0); bs16 tmp1_hi = DREG (src1) >> 16; bs16 tmp1_lo = DREG (src1); TRACE_INSN (cpu, "R%i.L = R%i.H = SIGN(R%i.H) * R%i.H + SIGN(R%i.L) * R%i.L;", dst0, dst0, src0, src1, src0, src1); if ((tmp0_hi >> 15) & 1) tmp1_hi = ~tmp1_hi + 1; if ((tmp0_lo >> 15) & 1) tmp1_lo = ~tmp1_lo + 1; tmp1_hi = tmp1_hi + tmp1_lo; STORE (DREG (dst0), REG_H_L (tmp1_hi << 16, tmp1_hi)); } else if (aopcde == 0) { bu32 s0 = DREG (src0); bu32 s1 = DREG (src1); bu32 s0h = s0 >> 16; bu32 s0l = s0 & 0xFFFF; bu32 s1h = s1 >> 16; bu32 s1l = s1 & 0xFFFF; bu32 t0, t1; bu32 ac1_i = 0, ac0_i = 0, v_i = 0, z_i = 0, n_i = 0; TRACE_INSN (cpu, "R%i = R%i %c|%c R%i%s;", dst0, src0, (aop & 2) ? '-' : '+', (aop & 1) ? '-' : '+', src1, amod0 (s, x)); if (aop & 2) t0 = sub16 (cpu, s0h, s1h, &ac1_i, &v_i, &z_i, &n_i, s, 0); else t0 = add16 (cpu, s0h, s1h, &ac1_i, &v_i, &z_i, &n_i, s, 0); if (aop & 1) t1 = sub16 (cpu, s0l, s1l, &ac0_i, &v_i, &z_i, &n_i, s, 0); else t1 = add16 (cpu, s0l, s1l, &ac0_i, &v_i, &z_i, &n_i, s, 0); SET_ASTATREG (ac1, ac1_i); SET_ASTATREG (ac0, ac0_i); SET_ASTATREG (az, z_i); SET_ASTATREG (an, n_i); SET_ASTATREG (v, v_i); if (v_i) SET_ASTATREG (vs, v_i); t0 &= 0xFFFF; t1 &= 0xFFFF; if (x) SET_DREG (dst0, (t1 << 16) | t0); else SET_DREG (dst0, (t0 << 16) | t1); } else if (aop == 1 && aopcde == 12) { bu32 val0 = ((AWREG (0) >> 16) + (AWREG (0) & 0xFFFF)) & 0xFFFF; bu32 val1 = ((AWREG (1) >> 16) + (AWREG (1) & 0xFFFF)) & 0xFFFF; TRACE_INSN (cpu, "R%i = A1.L + A1.H, R%i = A0.L + A0.H;", dst1, dst0); if (dst0 == dst1) illegal_instruction_combination (cpu); if (val0 & 0x8000) val0 |= 0xFFFF0000; if (val1 & 0x8000) val1 |= 0xFFFF0000; SET_DREG (dst0, val0); SET_DREG (dst1, val1); /* XXX: ASTAT ? */ } else if (aopcde == 1) { bu32 d0, d1; bu32 x0, x1; bu16 s0L = DREG (src0); bu16 s0H = DREG (src0) >> 16; bu16 s1L = DREG (src1); bu16 s1H = DREG (src1) >> 16; bu32 v_i = 0, n_i = 0, z_i = 0; TRACE_INSN (cpu, "R%i = R%i %s R%i, R%i = R%i %s R%i%s;", dst1, src0, HL ? "+|-" : "+|+", src1, dst0, src0, HL ? "-|+" : "-|-", src1, amod0amod2 (s, x, aop)); if (dst0 == dst1) illegal_instruction_combination (cpu); if (HL == 0) { x0 = add16 (cpu, s0H, s1H, 0, &v_i, &z_i, &n_i, s, aop) & 0xffff; x1 = add16 (cpu, s0L, s1L, 0, &v_i, &z_i, &n_i, s, aop) & 0xffff; d1 = (x0 << 16) | x1; x0 = sub16 (cpu, s0H, s1H, 0, &v_i, &z_i, &n_i, s, aop) & 0xffff; x1 = sub16 (cpu, s0L, s1L, 0, &v_i, &z_i, &n_i, s, aop) & 0xffff; if (x == 0) d0 = (x0 << 16) | x1; else d0 = (x1 << 16) | x0; } else { x0 = add16 (cpu, s0H, s1H, 0, &v_i, &z_i, &n_i, s, aop) & 0xffff; x1 = sub16 (cpu, s0L, s1L, 0, &v_i, &z_i, &n_i, s, aop) & 0xffff; d1 = (x0 << 16) | x1; x0 = sub16 (cpu, s0H, s1H, 0, &v_i, &z_i, &n_i, s, aop) & 0xffff; x1 = add16 (cpu, s0L, s1L, 0, &v_i, &z_i, &n_i, s, aop) & 0xffff; if (x == 0) d0 = (x0 << 16) | x1; else d0 = (x1 << 16) | x0; } SET_ASTATREG (az, z_i); SET_ASTATREG (an, n_i); SET_ASTATREG (v, v_i); if (v_i) SET_ASTATREG (vs, v_i); STORE (DREG (dst0), d0); STORE (DREG (dst1), d1); } else if ((aop == 0 || aop == 1 || aop == 2) && aopcde == 11) { bs40 acc0 = get_extended_acc (cpu, 0); bs40 acc1 = get_extended_acc (cpu, 1); bu32 v, dreg, sat = 0; bu32 carry = !!((bu40)~acc1 < (bu40)acc0); if (aop == 0) TRACE_INSN (cpu, "R%i = (A0 += A1);", dst0); else if (aop == 1) TRACE_INSN (cpu, "R%i.%c = (A0 += A1);", dst0, HL ? 'H' : 'L'); else TRACE_INSN (cpu, "A0 += A1%s;", s ? " (W32)" : ""); acc0 += acc1; acc0 = saturate_s40_astat (acc0, &v); if (aop == 2 && s == 1) /* A0 += A1 (W32) */ { if (acc0 & (bs40)0x8000000000ll) acc0 &= 0x80ffffffffll; else acc0 &= 0xffffffffll; } STORE (AXREG (0), acc0 >> 32); STORE (AWREG (0), acc0); SET_ASTATREG (av0, v && acc1); if (v) SET_ASTATREG (av0s, v); if (aop == 0 || aop == 1) { if (aop) /* Dregs_lo = A0 += A1 */ { dreg = saturate_s32 (rnd16 (acc0) << 16, &sat); if (HL) STORE (DREG (dst0), REG_H_L (dreg, DREG (dst0))); else STORE (DREG (dst0), REG_H_L (DREG (dst0), dreg >> 16)); } else /* Dregs = A0 += A1 */ { dreg = saturate_s32 (acc0, &sat); STORE (DREG (dst0), dreg); } STORE (ASTATREG (az), dreg == 0); STORE (ASTATREG (an), !!(dreg & 0x80000000)); STORE (ASTATREG (ac0), carry); STORE (ASTATREG (ac0_copy), carry); STORE (ASTATREG (v), sat); STORE (ASTATREG (v_copy), sat); if (sat) STORE (ASTATREG (vs), sat); } else { STORE (ASTATREG (az), acc0 == 0); STORE (ASTATREG (an), !!(acc0 & 0x8000000000ull)); STORE (ASTATREG (ac0), carry); STORE (ASTATREG (ac0_copy), carry); } } else if ((aop == 0 || aop == 1) && aopcde == 10) { TRACE_INSN (cpu, "R%i.L = A%i.X;", dst0, aop); SET_DREG_L (dst0, (bs8)AXREG (aop)); } else if (aop == 0 && aopcde == 4) { TRACE_INSN (cpu, "R%i = R%i + R%i%s;", dst0, src0, src1, amod1 (s, x)); SET_DREG (dst0, add32 (cpu, DREG (src0), DREG (src1), 1, s)); } else if (aop == 1 && aopcde == 4) { TRACE_INSN (cpu, "R%i = R%i - R%i%s;", dst0, src0, src1, amod1 (s, x)); SET_DREG (dst0, sub32 (cpu, DREG (src0), DREG (src1), 1, s, 0)); } else if (aop == 2 && aopcde == 4) { TRACE_INSN (cpu, "R%i = R%i + R%i, R%i = R%i - R%i%s;", dst1, src0, src1, dst0, src0, src1, amod1 (s, x)); if (dst0 == dst1) illegal_instruction_combination (cpu); STORE (DREG (dst1), add32 (cpu, DREG (src0), DREG (src1), 1, s)); STORE (DREG (dst0), sub32 (cpu, DREG (src0), DREG (src1), 1, s, 1)); } else if ((aop == 0 || aop == 1) && aopcde == 17) { bs40 acc0 = get_extended_acc (cpu, 0); bs40 acc1 = get_extended_acc (cpu, 1); bs40 val0, val1, sval0, sval1; bu32 sat, sat_i; TRACE_INSN (cpu, "R%i = A%i + A%i, R%i = A%i - A%i%s", dst1, !aop, aop, dst0, !aop, aop, amod1 (s, x)); TRACE_DECODE (cpu, "R%i = A%i:%#"PRIx64" + A%i:%#"PRIx64", " "R%i = A%i:%#"PRIx64" - A%i:%#"PRIx64"%s", dst1, !aop, aop ? acc0 : acc1, aop, aop ? acc1 : acc0, dst0, !aop, aop ? acc0 : acc1, aop, aop ? acc1 : acc0, amod1 (s, x)); if (dst0 == dst1) illegal_instruction_combination (cpu); val1 = acc0 + acc1; if (aop) val0 = acc0 - acc1; else val0 = acc1 - acc0; sval0 = saturate_s32 (val0, &sat); sat_i = sat; sval1 = saturate_s32 (val1, &sat); sat_i |= sat; if (s) { val0 = sval0; val1 = sval1; } STORE (DREG (dst0), val0); STORE (DREG (dst1), val1); SET_ASTATREG (v, sat_i); if (sat_i) SET_ASTATREG (vs, sat_i); SET_ASTATREG (an, val0 & 0x80000000 || val1 & 0x80000000); SET_ASTATREG (az, val0 == 0 || val1 == 0); SET_ASTATREG (ac1, (bu40)~acc0 < (bu40)acc1); if (aop) SET_ASTATREG (ac0, !!((bu40)acc1 <= (bu40)acc0)); else SET_ASTATREG (ac0, !!((bu40)acc0 <= (bu40)acc1)); } else if (aop == 0 && aopcde == 18) { bu40 acc0 = get_extended_acc (cpu, 0); bu40 acc1 = get_extended_acc (cpu, 1); bu32 s0L = DREG (src0); bu32 s0H = DREG (src0 + 1); bu32 s1L = DREG (src1); bu32 s1H = DREG (src1 + 1); bu32 s0, s1; bs16 tmp0, tmp1, tmp2, tmp3; /* This instruction is only defined for register pairs R1:0 and R3:2. */ if (!((src0 == 0 || src0 == 2) && (src1 == 0 || src1 == 2))) illegal_instruction (cpu); TRACE_INSN (cpu, "SAA (R%i:%i, R%i:%i)%s", src0 + 1, src0, src1 + 1, src1, s ? " (R)" :""); /* Bit s determines the order of the two registers from a pair: if s=0 the low-order bytes come from the low reg in the pair, and if s=1 the low-order bytes come from the high reg. */ if (s) { s0 = algn (s0H, s0L, IREG (0) & 3); s1 = algn (s1H, s1L, IREG (1) & 3); } else { s0 = algn (s0L, s0H, IREG (0) & 3); s1 = algn (s1L, s1H, IREG (1) & 3); } /* Find the absolute difference between pairs, make it absolute, then add it to the existing accumulator half. */ /* Byte 0 */ tmp0 = ((s0 << 24) >> 24) - ((s1 << 24) >> 24); tmp1 = ((s0 << 16) >> 24) - ((s1 << 16) >> 24); tmp2 = ((s0 << 8) >> 24) - ((s1 << 8) >> 24); tmp3 = ((s0 << 0) >> 24) - ((s1 << 0) >> 24); tmp0 = (tmp0 < 0) ? -tmp0 : tmp0; tmp1 = (tmp1 < 0) ? -tmp1 : tmp1; tmp2 = (tmp2 < 0) ? -tmp2 : tmp2; tmp3 = (tmp3 < 0) ? -tmp3 : tmp3; s0L = saturate_u16 ((bu32)tmp0 + ((acc0 >> 0) & 0xffff), 0); s0H = saturate_u16 ((bu32)tmp1 + ((acc0 >> 16) & 0xffff), 0); s1L = saturate_u16 ((bu32)tmp2 + ((acc1 >> 0) & 0xffff), 0); s1H = saturate_u16 ((bu32)tmp3 + ((acc1 >> 16) & 0xffff), 0); STORE (AWREG (0), (s0H << 16) | (s0L & 0xFFFF)); STORE (AXREG (0), 0); STORE (AWREG (1), (s1H << 16) | (s1L & 0xFFFF)); STORE (AXREG (1), 0); } else if (aop == 3 && aopcde == 18) { TRACE_INSN (cpu, "DISALGNEXCPT"); DIS_ALGN_EXPT |= 1; } else if ((aop == 0 || aop == 1) && aopcde == 20) { bu32 s0, s0L, s0H, s1, s1L, s1H; const char * const opts[] = { "", " (R)", " (T)", " (T, R)" }; TRACE_INSN (cpu, "R%i = BYTEOP1P (R%i:%i, R%i:%i)%s;", dst0, src0 + 1, src0, src1 + 1, src1, opts[s + (aop << 1)]); if (src0 == src1) illegal_instruction_combination (cpu); s0L = DREG (src0); s0H = DREG (src0 + 1); s1L = DREG (src1); s1H = DREG (src1 + 1); if (s) { s0 = algn (s0H, s0L, IREG (0) & 3); s1 = algn (s1H, s1L, IREG (1) & 3); } else { s0 = algn (s0L, s0H, IREG (0) & 3); s1 = algn (s1L, s1H, IREG (1) & 3); } SET_DREG (dst0, (((((s0 >> 0) & 0xff) + ((s1 >> 0) & 0xff) + !aop) >> 1) << 0) | (((((s0 >> 8) & 0xff) + ((s1 >> 8) & 0xff) + !aop) >> 1) << 8) | (((((s0 >> 16) & 0xff) + ((s1 >> 16) & 0xff) + !aop) >> 1) << 16) | (((((s0 >> 24) & 0xff) + ((s1 >> 24) & 0xff) + !aop) >> 1) << 24)); } else if (aop == 0 && aopcde == 21) { bu32 s0, s0L, s0H, s1, s1L, s1H; TRACE_INSN (cpu, "(R%i, R%i) = BYTEOP16P (R%i:%i, R%i:%i)%s;", dst1, dst0, src0 + 1, src0, src1 + 1, src1, s ? " (R)" : ""); if (dst0 == dst1) illegal_instruction_combination (cpu); s0L = DREG (src0); s0H = DREG (src0 + 1); s1L = DREG (src1); s1H = DREG (src1 + 1); if (s) { s0 = algn (s0H, s0L, IREG (0) & 3); s1 = algn (s1H, s1L, IREG (1) & 3); } else { s0 = algn (s0L, s0H, IREG (0) & 3); s1 = algn (s1L, s1H, IREG (1) & 3); } SET_DREG (dst0, ((((s0 >> 0) & 0xff) + ((s1 >> 0) & 0xff)) << 0) | ((((s0 >> 8) & 0xff) + ((s1 >> 8) & 0xff)) << 16)); SET_DREG (dst1, ((((s0 >> 16) & 0xff) + ((s1 >> 16) & 0xff)) << 0) | ((((s0 >> 24) & 0xff) + ((s1 >> 24) & 0xff)) << 16)); } else if (aop == 1 && aopcde == 21) { bu32 s0, s0L, s0H, s1, s1L, s1H; TRACE_INSN (cpu, "(R%i, R%i) = BYTEOP16M (R%i:%i, R%i:%i)%s;", dst1, dst0, src0 + 1, src0, src1 + 1, src1, s ? " (R)" : ""); if (dst0 == dst1) illegal_instruction_combination (cpu); s0L = DREG (src0); s0H = DREG (src0 + 1); s1L = DREG (src1); s1H = DREG (src1 + 1); if (s) { s0 = algn (s0H, s0L, IREG (0) & 3); s1 = algn (s1H, s1L, IREG (1) & 3); } else { s0 = algn (s0L, s0H, IREG (0) & 3); s1 = algn (s1L, s1H, IREG (1) & 3); } SET_DREG (dst0, (((((s0 >> 0) & 0xff) - ((s1 >> 0) & 0xff)) << 0) & 0xffff) | (((((s0 >> 8) & 0xff) - ((s1 >> 8) & 0xff)) << 16))); SET_DREG (dst1, (((((s0 >> 16) & 0xff) - ((s1 >> 16) & 0xff)) << 0) & 0xffff) | (((((s0 >> 24) & 0xff) - ((s1 >> 24) & 0xff)) << 16))); } else if (aop == 1 && aopcde == 7) { TRACE_INSN (cpu, "R%i = MIN (R%i, R%i);", dst0, src0, src1); SET_DREG (dst0, min32 (cpu, DREG (src0), DREG (src1))); } else if (aop == 0 && aopcde == 7) { TRACE_INSN (cpu, "R%i = MAX (R%i, R%i);", dst0, src0, src1); SET_DREG (dst0, max32 (cpu, DREG (src0), DREG (src1))); } else if (aop == 2 && aopcde == 7) { bu32 val = DREG (src0); int v; TRACE_INSN (cpu, "R%i = ABS R%i;", dst0, src0); if (val >> 31) val = -val; v = (val == 0x80000000); if (v) val = 0x7fffffff; SET_DREG (dst0, val); SET_ASTATREG (v, v); if (v) SET_ASTATREG (vs, 1); setflags_nz (cpu, val); } else if (aop == 3 && aopcde == 7) { bu32 val = DREG (src0); TRACE_INSN (cpu, "R%i = - R%i %s;", dst0, src0, amod1 (s, 0)); if (s && val == 0x80000000) { val = 0x7fffffff; SET_ASTATREG (v, 1); SET_ASTATREG (vs, 1); } else if (val == 0x80000000) val = 0x80000000; else val = -val; SET_DREG (dst0, val); SET_ASTATREG (az, val == 0); SET_ASTATREG (an, val & 0x80000000); } else if (aop == 2 && aopcde == 6) { bu32 in = DREG (src0); bu32 hi = (in & 0x80000000 ? (bu32)-(bs16)(in >> 16) : in >> 16) << 16; bu32 lo = (in & 0x8000 ? (bu32)-(bs16)(in & 0xFFFF) : in) & 0xFFFF; int v; TRACE_INSN (cpu, "R%i = ABS R%i (V);", dst0, src0); v = 0; if (hi == 0x80000000) { hi = 0x7fff0000; v = 1; } if (lo == 0x8000) { lo = 0x7fff; v = 1; } SET_DREG (dst0, hi | lo); SET_ASTATREG (v, v); if (v) SET_ASTATREG (vs, 1); setflags_nz_2x16 (cpu, DREG (dst0)); } else if (aop == 1 && aopcde == 6) { TRACE_INSN (cpu, "R%i = MIN (R%i, R%i) (V);", dst0, src0, src1); SET_DREG (dst0, min2x16 (cpu, DREG (src0), DREG (src1))); } else if (aop == 0 && aopcde == 6) { TRACE_INSN (cpu, "R%i = MAX (R%i, R%i) (V);", dst0, src0, src1); SET_DREG (dst0, max2x16 (cpu, DREG (src0), DREG (src1))); } else if (aop == 0 && aopcde == 24) { TRACE_INSN (cpu, "R%i = BYTEPACK (R%i, R%i);", dst0, src0, src1); SET_DREG (dst0, (((DREG (src0) >> 0) & 0xff) << 0) | (((DREG (src0) >> 16) & 0xff) << 8) | (((DREG (src1) >> 0) & 0xff) << 16) | (((DREG (src1) >> 16) & 0xff) << 24)); } else if (aop == 1 && aopcde == 24) { int order, lo, hi; bu64 comb_src; bu8 bytea, byteb, bytec, byted; TRACE_INSN (cpu, "(R%i, R%i) = BYTEUNPACK R%i:%i%s;", dst1, dst0, src0 + 1, src0, s ? " (R)" : ""); if (dst0 == dst1) illegal_instruction_combination (cpu); order = IREG (0) & 0x3; if (s) hi = src0, lo = src0 + 1; else hi = src0 + 1, lo = src0; comb_src = (((bu64)DREG (hi)) << 32) | DREG (lo); bytea = (comb_src >> (0 + 8 * order)); byteb = (comb_src >> (8 + 8 * order)); bytec = (comb_src >> (16 + 8 * order)); byted = (comb_src >> (24 + 8 * order)); SET_DREG (dst0, bytea | ((bu32)byteb << 16)); SET_DREG (dst1, bytec | ((bu32)byted << 16)); } else if (aopcde == 13) { const char *searchmodes[] = { "GT", "GE", "LT", "LE" }; bool up_hi, up_lo; bs16 a0_lo, a1_lo, src_hi, src_lo; TRACE_INSN (cpu, "(R%i, R%i) = SEARCH R%i (%s);", dst1, dst0, src0, searchmodes[aop]); if (dst0 == dst1) illegal_instruction_combination (cpu); up_hi = up_lo = false; a0_lo = AWREG (0); a1_lo = AWREG (1); src_lo = DREG (src0); src_hi = DREG (src0) >> 16; switch (aop) { case 0: up_hi = (src_hi > a1_lo); up_lo = (src_lo > a0_lo); break; case 1: up_hi = (src_hi >= a1_lo); up_lo = (src_lo >= a0_lo); break; case 2: up_hi = (src_hi < a1_lo); up_lo = (src_lo < a0_lo); break; case 3: up_hi = (src_hi <= a1_lo); up_lo = (src_lo <= a0_lo); break; } if (up_hi) { SET_AREG (1, src_hi); SET_DREG (dst1, PREG (0)); } if (up_lo) { SET_AREG (0, src_lo); SET_DREG (dst0, PREG (0)); } } else illegal_instruction (cpu); } static void decode_dsp32shift_0 (SIM_CPU *cpu, bu16 iw0, bu16 iw1) { /* dsp32shift +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 0 | 0 |.M.| 1 | 1 | 0 | 0 | - | - |.sopcde............| |.sop...|.HLs...|.dst0......| - | - | - |.src0......|.src1......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int HLs = ((iw1 >> DSP32Shift_HLs_bits) & DSP32Shift_HLs_mask); int sop = ((iw1 >> DSP32Shift_sop_bits) & DSP32Shift_sop_mask); int src0 = ((iw1 >> DSP32Shift_src0_bits) & DSP32Shift_src0_mask); int src1 = ((iw1 >> DSP32Shift_src1_bits) & DSP32Shift_src1_mask); int dst0 = ((iw1 >> DSP32Shift_dst0_bits) & DSP32Shift_dst0_mask); int sopcde = ((iw0 >> (DSP32Shift_sopcde_bits - 16)) & DSP32Shift_sopcde_mask); int M = ((iw0 >> (DSP32Shift_M_bits - 16)) & DSP32Shift_M_mask); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_dsp32shift); TRACE_EXTRACT (cpu, "%s: M:%i sopcde:%i sop:%i HLs:%i dst0:%i src0:%i src1:%i", __func__, M, sopcde, sop, HLs, dst0, src0, src1); if ((sop == 0 || sop == 1) && sopcde == 0) { bu16 val; bs32 shft = (bs8)(DREG (src0) << 2) >> 2; TRACE_INSN (cpu, "R%i.%c = ASHIFT R%i.%c BY R%i.L%s;", dst0, HLs < 2 ? 'L' : 'H', src1, HLs & 1 ? 'H' : 'L', src0, sop == 1 ? " (S)" : ""); if ((HLs & 1) == 0) val = (bu16)(DREG (src1) & 0xFFFF); else val = (bu16)((DREG (src1) & 0xFFFF0000) >> 16); /* Positive shift magnitudes produce Logical Left shifts. Negative shift magnitudes produce Arithmetic Right shifts. */ if (shft <= 0) val = ashiftrt (cpu, val, -shft, 16); else val = lshift (cpu, val, shft, 16, sop == 1); if ((HLs & 2) == 0) STORE (DREG (dst0), REG_H_L (DREG (dst0), val)); else STORE (DREG (dst0), REG_H_L (val << 16, DREG (dst0))); } else if (sop == 2 && sopcde == 0) { bs32 shft = (bs8)(DREG (src0) << 2) >> 2; bu16 val; TRACE_INSN (cpu, "R%i.%c = LSHIFT R%i.%c BY R%i.L;", dst0, HLs < 2 ? 'L' : 'H', src1, HLs & 1 ? 'H' : 'L', src0); if ((HLs & 1) == 0) val = (bu16)(DREG (src1) & 0xFFFF); else val = (bu16)((DREG (src1) & 0xFFFF0000) >> 16); if (shft < 0) val = val >> (-1 * shft); else val = val << shft; if ((HLs & 2) == 0) SET_DREG (dst0, REG_H_L (DREG (dst0), val)); else SET_DREG (dst0, REG_H_L (val << 16, DREG (dst0))); SET_ASTATREG (az, !((val & 0xFFFF0000) == 0) || ((val & 0xFFFF) == 0)); SET_ASTATREG (an, (!!(val & 0x80000000)) ^ (!!(val & 0x8000))); SET_ASTATREG (v, 0); } else if (sop == 2 && sopcde == 3 && (HLs == 1 || HLs == 0)) { int shift = imm6 (DREG (src0) & 0xFFFF); bu32 cc = CCREG; bu40 acc = get_unextended_acc (cpu, HLs); TRACE_INSN (cpu, "A%i = ROT A%i BY R%i.L;", HLs, HLs, src0); TRACE_DECODE (cpu, "A%i:%#"PRIx64" shift:%i CC:%i", HLs, acc, shift, cc); acc = rot40 (acc, shift, &cc); SET_AREG (HLs, acc); if (shift) SET_CCREG (cc); } else if (sop == 0 && sopcde == 3 && (HLs == 0 || HLs == 1)) { bs32 shft = (bs8)(DREG (src0) << 2) >> 2; bu64 val = get_extended_acc (cpu, HLs); HLs = !!HLs; TRACE_INSN (cpu, "A%i = ASHIFT A%i BY R%i.L;", HLs, HLs, src0); TRACE_DECODE (cpu, "A%i:%#"PRIx64" shift:%i", HLs, val, shft); if (shft <= 0) val = ashiftrt (cpu, val, -shft, 40); else val = lshift (cpu, val, shft, 40, 0); STORE (AXREG (HLs), (val >> 32) & 0xff); STORE (AWREG (HLs), (val & 0xffffffff)); } else if (sop == 1 && sopcde == 3 && (HLs == 0 || HLs == 1)) { bs32 shft = (bs8)(DREG (src0) << 2) >> 2; bu64 val; HLs = !!HLs; TRACE_INSN (cpu, "A%i = LSHIFT A%i BY R%i.L;", HLs, HLs, src0); val = get_extended_acc (cpu, HLs); if (shft <= 0) val = lshiftrt (cpu, val, -shft, 40); else val = lshift (cpu, val, shft, 40, 0); STORE (AXREG (HLs), (val >> 32) & 0xff); STORE (AWREG (HLs), (val & 0xffffffff)); } else if ((sop == 0 || sop == 1) && sopcde == 1) { bs32 shft = (bs8)(DREG (src0) << 2) >> 2; bu16 val0, val1; bu32 astat; TRACE_INSN (cpu, "R%i = ASHIFT R%i BY R%i.L (V%s);", dst0, src1, src0, sop == 1 ? ",S" : ""); val0 = (bu16)DREG (src1) & 0xFFFF; val1 = (bu16)((DREG (src1) & 0xFFFF0000) >> 16); if (shft <= 0) { val0 = ashiftrt (cpu, val0, -shft, 16); astat = ASTAT; val1 = ashiftrt (cpu, val1, -shft, 16); } else { val0 = lshift (cpu, val0, shft, 16, sop == 1); astat = ASTAT; val1 = lshift (cpu, val1, shft, 16, sop == 1); } SET_ASTAT (ASTAT | astat); STORE (DREG (dst0), (val1 << 16) | val0); } else if ((sop == 0 || sop == 1 || sop == 2) && sopcde == 2) { /* dregs = [LA]SHIFT dregs BY dregs_lo (opt_S) */ /* sop == 1 : opt_S */ bu32 v = DREG (src1); /* LSHIFT uses sign extended low 6 bits of dregs_lo. */ bs32 shft = (bs8)(DREG (src0) << 2) >> 2; TRACE_INSN (cpu, "R%i = %cSHIFT R%i BY R%i.L%s;", dst0, shft && sop != 2 ? 'A' : 'L', src1, src0, sop == 1 ? " (S)" : ""); if (shft < 0) { if (sop == 2) STORE (DREG (dst0), lshiftrt (cpu, v, -shft, 32)); else STORE (DREG (dst0), ashiftrt (cpu, v, -shft, 32)); } else STORE (DREG (dst0), lshift (cpu, v, shft, 32, sop == 1)); } else if (sop == 3 && sopcde == 2) { int shift = imm6 (DREG (src0) & 0xFFFF); bu32 src = DREG (src1); bu32 ret, cc = CCREG; TRACE_INSN (cpu, "R%i = ROT R%i BY R%i.L;", dst0, src1, src0); TRACE_DECODE (cpu, "R%i:%#x R%i:%#x shift:%i CC:%i", dst0, DREG (dst0), src1, src, shift, cc); ret = rot32 (src, shift, &cc); STORE (DREG (dst0), ret); if (shift) SET_CCREG (cc); } else if (sop == 2 && sopcde == 1) { bs32 shft = (bs8)(DREG (src0) << 2) >> 2; bu16 val0, val1; bu32 astat; TRACE_INSN (cpu, "R%i = LSHIFT R%i BY R%i.L (V);", dst0, src1, src0); val0 = (bu16)DREG (src1) & 0xFFFF; val1 = (bu16)((DREG (src1) & 0xFFFF0000) >> 16); if (shft <= 0) { val0 = lshiftrt (cpu, val0, -shft, 16); astat = ASTAT; val1 = lshiftrt (cpu, val1, -shft, 16); } else { val0 = lshift (cpu, val0, shft, 16, 0); astat = ASTAT; val1 = lshift (cpu, val1, shft, 16, 0); } SET_ASTAT (ASTAT | astat); STORE (DREG (dst0), (val1 << 16) | val0); } else if (sopcde == 4) { bu32 sv0 = DREG (src0); bu32 sv1 = DREG (src1); TRACE_INSN (cpu, "R%i = PACK (R%i.%c, R%i.%c);", dst0, src1, sop & 2 ? 'H' : 'L', src0, sop & 1 ? 'H' : 'L'); if (sop & 1) sv0 >>= 16; if (sop & 2) sv1 >>= 16; SET_DREG (dst0, (sv1 << 16) | (sv0 & 0xFFFF)); } else if (sop == 0 && sopcde == 5) { bu32 sv1 = DREG (src1); TRACE_INSN (cpu, "R%i.L = SIGNBITS R%i;", dst0, src1); SET_DREG_L (dst0, signbits (sv1, 32)); } else if (sop == 1 && sopcde == 5) { bu32 sv1 = DREG (src1); TRACE_INSN (cpu, "R%i.L = SIGNBITS R%i.L;", dst0, src1); SET_DREG_L (dst0, signbits (sv1, 16)); } else if (sop == 2 && sopcde == 5) { bu32 sv1 = DREG (src1); TRACE_INSN (cpu, "R%i.L = SIGNBITS R%i.H;", dst0, src1); SET_DREG_L (dst0, signbits (sv1 >> 16, 16)); } else if ((sop == 0 || sop == 1) && sopcde == 6) { bu64 acc = AXREG (sop); TRACE_INSN (cpu, "R%i.L = SIGNBITS A%i;", dst0, sop); acc <<= 32; acc |= AWREG (sop); SET_DREG_L (dst0, signbits (acc, 40) & 0xFFFF); } else if (sop == 3 && sopcde == 6) { bu32 v = ones (DREG (src1)); TRACE_INSN (cpu, "R%i.L = ONES R%i;", dst0, src1); SET_DREG_L (dst0, v); } else if (sop == 0 && sopcde == 7) { bu16 sv1 = (bu16)signbits (DREG (src1), 32); bu16 sv0 = (bu16)DREG (src0); bu16 dst_lo; TRACE_INSN (cpu, "R%i.L = EXPADJ (R%i, R%i.L);", dst0, src1, src0); if ((sv1 & 0x1f) < (sv0 & 0x1f)) dst_lo = sv1; else dst_lo = sv0; STORE (DREG (dst0), REG_H_L (DREG (dst0), dst_lo)); } else if (sop == 1 && sopcde == 7) { /* Exponent adjust on two 16-bit inputs. Select smallest norm among 3 inputs. */ bs16 src1_hi = (DREG (src1) & 0xFFFF0000) >> 16; bs16 src1_lo = (DREG (src1) & 0xFFFF); bu16 src0_lo = (DREG (src0) & 0xFFFF); bu16 tmp_hi, tmp_lo, tmp; TRACE_INSN (cpu, "R%i.L = EXPADJ (R%i, R%i.L) (V);", dst0, src1, src0); tmp_hi = signbits (src1_hi, 16); tmp_lo = signbits (src1_lo, 16); if ((tmp_hi & 0xf) < (tmp_lo & 0xf)) if ((tmp_hi & 0xf) < (src0_lo & 0xf)) tmp = tmp_hi; else tmp = src0_lo; else if ((tmp_lo & 0xf) < (src0_lo & 0xf)) tmp = tmp_lo; else tmp = src0_lo; STORE (DREG (dst0), REG_H_L (DREG (dst0), tmp)); } else if (sop == 2 && sopcde == 7) { /* Exponent adjust on single 16-bit register. */ bu16 tmp; bu16 src0_lo = (bu16)(DREG (src0) & 0xFFFF); TRACE_INSN (cpu, "R%i.L = EXPADJ (R%i.L, R%i.L);", dst0, src1, src0); tmp = signbits (DREG (src1) & 0xFFFF, 16); if ((tmp & 0xf) < (src0_lo & 0xf)) SET_DREG_L (dst0, tmp); else SET_DREG_L (dst0, src0_lo); } else if (sop == 3 && sopcde == 7) { bu16 tmp; bu16 src0_lo = (bu16)(DREG (src0) & 0xFFFF); TRACE_INSN (cpu, "R%i.L = EXPADJ (R%i.H, R%i.L);", dst0, src1, src0); tmp = signbits ((DREG (src1) & 0xFFFF0000) >> 16, 16); if ((tmp & 0xf) < (src0_lo & 0xf)) SET_DREG_L (dst0, tmp); else SET_DREG_L (dst0, src0_lo); } else if (sop == 0 && sopcde == 8) { bu64 acc = get_unextended_acc (cpu, 0); bu32 s0, s1; TRACE_INSN (cpu, "BITMUX (R%i, R%i, A0) (ASR);", src0, src1); if (src0 == src1) illegal_instruction_combination (cpu); s0 = DREG (src0); s1 = DREG (src1); acc = (acc >> 2) | (((bu64)s0 & 1) << 38) | (((bu64)s1 & 1) << 39); SET_DREG (src0, s0 >> 1); SET_DREG (src1, s1 >> 1); SET_AREG (0, acc); } else if (sop == 1 && sopcde == 8) { bu64 acc = get_unextended_acc (cpu, 0); bu32 s0, s1; TRACE_INSN (cpu, "BITMUX (R%i, R%i, A0) (ASL);", src0, src1); if (src0 == src1) illegal_instruction_combination (cpu); s0 = DREG (src0); s1 = DREG (src1); acc = (acc << 2) | ((s0 >> 31) & 1) | ((s1 >> 30) & 2); SET_DREG (src0, s0 << 1); SET_DREG (src1, s1 << 1); SET_AREG (0, acc); } else if ((sop == 0 || sop == 1) && sopcde == 9) { bs40 acc0 = get_extended_acc (cpu, 0); bs16 sL, sH, out; TRACE_INSN (cpu, "R%i.L = VIT_MAX (R%i) (AS%c);", dst0, src1, sop & 1 ? 'R' : 'L'); sL = DREG (src1); sH = DREG (src1) >> 16; if (sop & 1) acc0 >>= 1; else acc0 <<= 1; if (((sH - sL) & 0x8000) == 0) { out = sH; acc0 |= (sop & 1) ? 0x80000000 : 1; } else out = sL; SET_AREG (0, acc0); SET_DREG (dst0, REG_H_L (DREG (dst0), out)); } else if ((sop == 2 || sop == 3) && sopcde == 9) { bs40 acc0 = get_extended_acc (cpu, 0); bs16 s0L, s0H, s1L, s1H, out0, out1; TRACE_INSN (cpu, "R%i = VIT_MAX (R%i, R%i) (AS%c);", dst0, src1, src0, sop & 1 ? 'R' : 'L'); s0L = DREG (src0); s0H = DREG (src0) >> 16; s1L = DREG (src1); s1H = DREG (src1) >> 16; if (sop & 1) acc0 >>= 2; else acc0 <<= 2; if (((s0H - s0L) & 0x8000) == 0) { out0 = s0H; acc0 |= (sop & 1) ? 0x40000000 : 2; } else out0 = s0L; if (((s1H - s1L) & 0x8000) == 0) { out1 = s1H; acc0 |= (sop & 1) ? 0x80000000 : 1; } else out1 = s1L; SET_AREG (0, acc0); SET_DREG (dst0, REG_H_L (out1 << 16, out0)); } else if (sop == 0 && sopcde == 10) { bu32 v = DREG (src0); bu32 x = DREG (src1); bu32 mask = (1 << (v & 0x1f)) - 1; TRACE_INSN (cpu, "R%i = EXTRACT (R%i, R%i.L) (Z);", dst0, src1, src0); x >>= ((v >> 8) & 0x1f); SET_DREG (dst0, x & mask); setflags_logical (cpu, DREG (dst0)); } else if (sop == 1 && sopcde == 10) { bu32 v = DREG (src0); bu32 x = DREG (src1); bu32 sgn = (1 << (v & 0x1f)) >> 1; bu32 mask = (1 << (v & 0x1f)) - 1; TRACE_INSN (cpu, "R%i = EXTRACT (R%i, R%i.L) (X);", dst0, src1, src0); x >>= ((v >> 8) & 0x1f); x &= mask; if (x & sgn) x |= ~mask; SET_DREG (dst0, x); setflags_logical (cpu, DREG (dst0)); } else if ((sop == 2 || sop == 3) && sopcde == 10) { /* The first dregs is the "background" while the second dregs is the "foreground". The fg reg is used to overlay the bg reg and is: | nnnn nnnn | nnnn nnnn | xxxp pppp | xxxL LLLL | n = the fg bit field p = bit position in bg reg to start LSB of fg field L = number of fg bits to extract Using (X) sign-extends the fg bit field. */ bu32 fg = DREG (src0); bu32 bg = DREG (src1); bu32 len = fg & 0x1f; bu32 mask = (1 << MIN (16, len)) - 1; bu32 fgnd = (fg >> 16) & mask; int shft = ((fg >> 8) & 0x1f); TRACE_INSN (cpu, "R%i = DEPOSIT (R%i, R%i)%s;", dst0, src1, src0, sop == 3 ? " (X)" : ""); if (sop == 3) { /* Sign extend the fg bit field. */ mask = -1; fgnd = ((bs32)(bs16)(fgnd << (16 - len))) >> (16 - len); } fgnd <<= shft; mask <<= shft; bg &= ~mask; SET_DREG (dst0, bg | fgnd); setflags_logical (cpu, DREG (dst0)); } else if (sop == 0 && sopcde == 11) { bu64 acc0 = get_unextended_acc (cpu, 0); TRACE_INSN (cpu, "R%i.L = CC = BXORSHIFT (A0, R%i);", dst0, src0); acc0 <<= 1; SET_CCREG (xor_reduce (acc0, DREG (src0))); SET_DREG (dst0, REG_H_L (DREG (dst0), CCREG)); SET_AREG (0, acc0); } else if (sop == 1 && sopcde == 11) { bu64 acc0 = get_unextended_acc (cpu, 0); TRACE_INSN (cpu, "R%i.L = CC = BXOR (A0, R%i);", dst0, src0); SET_CCREG (xor_reduce (acc0, DREG (src0))); SET_DREG (dst0, REG_H_L (DREG (dst0), CCREG)); } else if (sop == 0 && sopcde == 12) { bu64 acc0 = get_unextended_acc (cpu, 0); bu64 acc1 = get_unextended_acc (cpu, 1); TRACE_INSN (cpu, "A0 = BXORSHIFT (A0, A1, CC);"); acc0 = (acc0 << 1) | (CCREG ^ xor_reduce (acc0, acc1)); SET_AREG (0, acc0); } else if (sop == 1 && sopcde == 12) { bu64 acc0 = get_unextended_acc (cpu, 0); bu64 acc1 = get_unextended_acc (cpu, 1); TRACE_INSN (cpu, "R%i.L = CC = BXOR (A0, A1, CC);", dst0); SET_CCREG (CCREG ^ xor_reduce (acc0, acc1)); acc0 = (acc0 << 1) | CCREG; SET_DREG (dst0, REG_H_L (DREG (dst0), CCREG)); } else if ((sop == 0 || sop == 1 || sop == 2) && sopcde == 13) { int shift = (sop + 1) * 8; TRACE_INSN (cpu, "R%i = ALIGN%i (R%i, R%i);", dst0, shift, src1, src0); SET_DREG (dst0, (DREG (src1) << (32 - shift)) | (DREG (src0) >> shift)); } else illegal_instruction (cpu); } static void decode_dsp32shiftimm_0 (SIM_CPU *cpu, bu16 iw0, bu16 iw1) { /* dsp32shiftimm +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 0 | 0 |.M.| 1 | 1 | 0 | 1 | - | - |.sopcde............| |.sop...|.HLs...|.dst0......|.immag.................|.src1......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int src1 = ((iw1 >> DSP32ShiftImm_src1_bits) & DSP32ShiftImm_src1_mask); int sop = ((iw1 >> DSP32ShiftImm_sop_bits) & DSP32ShiftImm_sop_mask); int bit8 = ((iw1 >> 8) & 0x1); int immag = ((iw1 >> DSP32ShiftImm_immag_bits) & DSP32ShiftImm_immag_mask); int newimmag = (-(iw1 >> DSP32ShiftImm_immag_bits) & DSP32ShiftImm_immag_mask); int dst0 = ((iw1 >> DSP32ShiftImm_dst0_bits) & DSP32ShiftImm_dst0_mask); int M = ((iw0 >> (DSP32ShiftImm_M_bits - 16)) & DSP32ShiftImm_M_mask); int sopcde = ((iw0 >> (DSP32ShiftImm_sopcde_bits - 16)) & DSP32ShiftImm_sopcde_mask); int HLs = ((iw1 >> DSP32ShiftImm_HLs_bits) & DSP32ShiftImm_HLs_mask); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_dsp32shiftimm); TRACE_EXTRACT (cpu, "%s: M:%i sopcde:%i sop:%i HLs:%i dst0:%i immag:%#x src1:%i", __func__, M, sopcde, sop, HLs, dst0, immag, src1); if (sopcde == 0) { bu16 in = DREG (src1) >> ((HLs & 1) ? 16 : 0); bu16 result; bu32 v; if (sop == 0) { TRACE_INSN (cpu, "R%i.%c = R%i.%c >>> %i;", dst0, (HLs & 2) ? 'H' : 'L', src1, (HLs & 1) ? 'H' : 'L', newimmag); result = ashiftrt (cpu, in, newimmag, 16); } else if (sop == 1 && bit8 == 0) { TRACE_INSN (cpu, "R%i.%c = R%i.%c << %i (S);", dst0, (HLs & 2) ? 'H' : 'L', src1, (HLs & 1) ? 'H' : 'L', immag); result = lshift (cpu, in, immag, 16, 1); } else if (sop == 1 && bit8) { TRACE_INSN (cpu, "R%i.%c = R%i.%c >>> %i (S);", dst0, (HLs & 2) ? 'H' : 'L', src1, (HLs & 1) ? 'H' : 'L', immag); result = lshift (cpu, in, immag, 16, 1); } else if (sop == 2 && bit8) { TRACE_INSN (cpu, "R%i.%c = R%i.%c >> %i;", dst0, (HLs & 2) ? 'H' : 'L', src1, (HLs & 1) ? 'H' : 'L', newimmag); result = lshiftrt (cpu, in, newimmag, 16); } else if (sop == 2 && bit8 == 0) { TRACE_INSN (cpu, "R%i.%c = R%i.%c << %i;", dst0, (HLs & 2) ? 'H' : 'L', src1, (HLs & 1) ? 'H' : 'L', immag); result = lshift (cpu, in, immag, 16, 0); } else illegal_instruction (cpu); v = DREG (dst0); if (HLs & 2) STORE (DREG (dst0), (v & 0xFFFF) | (result << 16)); else STORE (DREG (dst0), (v & 0xFFFF0000) | result); } else if (sop == 2 && sopcde == 3 && (HLs == 1 || HLs == 0)) { int shift = imm6 (immag); bu32 cc = CCREG; bu40 acc = get_unextended_acc (cpu, HLs); TRACE_INSN (cpu, "A%i = ROT A%i BY %i;", HLs, HLs, shift); TRACE_DECODE (cpu, "A%i:%#"PRIx64" shift:%i CC:%i", HLs, acc, shift, cc); acc = rot40 (acc, shift, &cc); SET_AREG (HLs, acc); if (shift) SET_CCREG (cc); } else if (sop == 0 && sopcde == 3 && bit8 == 1) { /* Arithmetic shift, so shift in sign bit copies. */ bu64 acc; int shift = uimm5 (newimmag); HLs = !!HLs; TRACE_INSN (cpu, "A%i = A%i >>> %i;", HLs, HLs, shift); acc = get_extended_acc (cpu, HLs); acc >>= shift; /* Sign extend again. */ if (acc & (1ULL << 39)) acc |= -(1ULL << 39); else acc &= ~(-(1ULL << 39)); STORE (AXREG (HLs), (acc >> 32) & 0xFF); STORE (AWREG (HLs), acc & 0xFFFFFFFF); } else if ((sop == 0 && sopcde == 3 && bit8 == 0) || (sop == 1 && sopcde == 3)) { bu64 acc; int shiftup = uimm5 (immag); int shiftdn = uimm5 (newimmag); HLs = !!HLs; TRACE_INSN (cpu, "A%i = A%i %s %i;", HLs, HLs, sop == 0 ? "<<" : ">>", sop == 0 ? shiftup : shiftdn); acc = AXREG (HLs); /* Logical shift, so shift in zeroes. */ acc &= 0xFF; acc <<= 32; acc |= AWREG (HLs); if (sop == 0) acc <<= shiftup; else acc >>= shiftdn; SET_AREG (HLs, acc); SET_ASTATREG (an, !!(acc & 0x8000000000ull)); SET_ASTATREG (az, acc == 0); } else if (sop == 1 && sopcde == 1 && bit8 == 0) { int count = imm5 (immag); bu16 val0 = DREG (src1) >> 16; bu16 val1 = DREG (src1) & 0xFFFF; bu32 astat; TRACE_INSN (cpu, "R%i = R%i << %i (V,S);", dst0, src1, count); val0 = lshift (cpu, val0, count, 16, 1); astat = ASTAT; val1 = lshift (cpu, val1, count, 16, 1); SET_ASTAT (ASTAT | astat); STORE (DREG (dst0), (val0 << 16) | val1); } else if (sop == 2 && sopcde == 1 && bit8 == 1) { int count = imm5 (newimmag); bu16 val0 = DREG (src1) & 0xFFFF; bu16 val1 = DREG (src1) >> 16; bu32 astat; TRACE_INSN (cpu, "R%i = R%i >> %i (V);", dst0, src1, count); val0 = lshiftrt (cpu, val0, count, 16); astat = ASTAT; val1 = lshiftrt (cpu, val1, count, 16); SET_ASTAT (ASTAT | astat); STORE (DREG (dst0), val0 | (val1 << 16)); } else if (sop == 2 && sopcde == 1 && bit8 == 0) { int count = imm5 (immag); bu16 val0 = DREG (src1) & 0xFFFF; bu16 val1 = DREG (src1) >> 16; bu32 astat; TRACE_INSN (cpu, "R%i = R%i << %i (V);", dst0, src1, count); val0 = lshift (cpu, val0, count, 16, 0); astat = ASTAT; val1 = lshift (cpu, val1, count, 16, 0); SET_ASTAT (ASTAT | astat); STORE (DREG (dst0), val0 | (val1 << 16)); } else if (sopcde == 1 && (sop == 0 || (sop == 1 && bit8 == 1))) { int count = uimm5 (newimmag); bu16 val0 = DREG (src1) & 0xFFFF; bu16 val1 = DREG (src1) >> 16; bu32 astat; TRACE_INSN (cpu, "R%i = R%i >>> %i %s;", dst0, src1, count, sop == 0 ? "(V)" : "(V,S)"); val0 = ashiftrt (cpu, val0, count, 16); astat = ASTAT; val1 = ashiftrt (cpu, val1, count, 16); SET_ASTAT (ASTAT | astat); STORE (DREG (dst0), REG_H_L (val1 << 16, val0)); } else if (sop == 1 && sopcde == 2) { int count = imm6 (immag); TRACE_INSN (cpu, "R%i = R%i << %i (S);", dst0, src1, count); STORE (DREG (dst0), lshift (cpu, DREG (src1), count, 32, 1)); } else if (sop == 2 && sopcde == 2) { int count = imm6 (newimmag); TRACE_INSN (cpu, "R%i = R%i >> %i;", dst0, src1, count); if (count < 0) STORE (DREG (dst0), lshift (cpu, DREG (src1), -count, 32, 0)); else STORE (DREG (dst0), lshiftrt (cpu, DREG (src1), count, 32)); } else if (sop == 3 && sopcde == 2) { int shift = imm6 (immag); bu32 src = DREG (src1); bu32 ret, cc = CCREG; TRACE_INSN (cpu, "R%i = ROT R%i BY %i;", dst0, src1, shift); TRACE_DECODE (cpu, "R%i:%#x R%i:%#x shift:%i CC:%i", dst0, DREG (dst0), src1, src, shift, cc); ret = rot32 (src, shift, &cc); STORE (DREG (dst0), ret); if (shift) SET_CCREG (cc); } else if (sop == 0 && sopcde == 2) { int count = imm6 (newimmag); TRACE_INSN (cpu, "R%i = R%i >>> %i;", dst0, src1, count); if (count < 0) STORE (DREG (dst0), lshift (cpu, DREG (src1), -count, 32, 0)); else STORE (DREG (dst0), ashiftrt (cpu, DREG (src1), count, 32)); } else illegal_instruction (cpu); } static void outc (SIM_CPU *cpu, char ch) { SIM_DESC sd = CPU_STATE (cpu); sim_io_printf (sd, "%c", ch); if (ch == '\n') sim_io_flush_stdout (sd); } static void decode_psedoDEBUG_0 (SIM_CPU *cpu, bu16 iw0) { /* psedoDEBUG +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 |.fn....|.grp.......|.reg.......| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ SIM_DESC sd = CPU_STATE (cpu); int fn = ((iw0 >> PseudoDbg_fn_bits) & PseudoDbg_fn_mask); int grp = ((iw0 >> PseudoDbg_grp_bits) & PseudoDbg_grp_mask); int reg = ((iw0 >> PseudoDbg_reg_bits) & PseudoDbg_reg_mask); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_psedoDEBUG); TRACE_EXTRACT (cpu, "%s: fn:%i grp:%i reg:%i", __func__, fn, grp, reg); if ((reg == 0 || reg == 1) && fn == 3) { TRACE_INSN (cpu, "DBG A%i;", reg); sim_io_printf (sd, "DBG : A%i = %#"PRIx64"\n", reg, get_unextended_acc (cpu, reg)); } else if (reg == 3 && fn == 3) { TRACE_INSN (cpu, "ABORT;"); cec_exception (cpu, VEC_SIM_ABORT); SET_DREG (0, 1); } else if (reg == 4 && fn == 3) { TRACE_INSN (cpu, "HLT;"); cec_exception (cpu, VEC_SIM_HLT); SET_DREG (0, 0); } else if (reg == 5 && fn == 3) unhandled_instruction (cpu, "DBGHALT"); else if (reg == 6 && fn == 3) unhandled_instruction (cpu, "DBGCMPLX (dregs)"); else if (reg == 7 && fn == 3) unhandled_instruction (cpu, "DBG"); else if (grp == 0 && fn == 2) { TRACE_INSN (cpu, "OUTC R%i;", reg); outc (cpu, DREG (reg)); } else if (fn == 0) { const char *reg_name = get_allreg_name (grp, reg); TRACE_INSN (cpu, "DBG %s;", reg_name); sim_io_printf (sd, "DBG : %s = 0x%08x\n", reg_name, reg_read (cpu, grp, reg)); } else if (fn == 1) unhandled_instruction (cpu, "PRNT allregs"); else illegal_instruction (cpu); } static void decode_psedoOChar_0 (SIM_CPU *cpu, bu16 iw0) { /* psedoOChar +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 1 |.ch............................| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ int ch = ((iw0 >> PseudoChr_ch_bits) & PseudoChr_ch_mask); PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_psedoOChar); TRACE_EXTRACT (cpu, "%s: ch:%#x", __func__, ch); TRACE_INSN (cpu, "OUTC %#x;", ch); outc (cpu, ch); } static void decode_psedodbg_assert_0 (SIM_CPU *cpu, bu16 iw0, bu16 iw1, bu32 pc) { /* psedodbg_assert +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ | 1 | 1 | 1 | 1 | 0 | - | - | - | dbgop |.grp.......|.regtest...| |.expected......................................................| +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */ SIM_DESC sd = CPU_STATE (cpu); int expected = ((iw1 >> PseudoDbg_Assert_expected_bits) & PseudoDbg_Assert_expected_mask); int dbgop = ((iw0 >> (PseudoDbg_Assert_dbgop_bits - 16)) & PseudoDbg_Assert_dbgop_mask); int grp = ((iw0 >> (PseudoDbg_Assert_grp_bits - 16)) & PseudoDbg_Assert_grp_mask); int regtest = ((iw0 >> (PseudoDbg_Assert_regtest_bits - 16)) & PseudoDbg_Assert_regtest_mask); bu16 actual; bu32 val = reg_read (cpu, grp, regtest); const char *reg_name = get_allreg_name (grp, regtest); const char *dbg_name, *dbg_appd; PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_psedodbg_assert); TRACE_EXTRACT (cpu, "%s: dbgop:%i grp:%i regtest:%i expected:%#x", __func__, dbgop, grp, regtest, expected); if (dbgop == 0 || dbgop == 2) { dbg_name = dbgop == 0 ? "DBGA" : "DBGAL"; dbg_appd = dbgop == 0 ? ".L" : ""; actual = val; } else if (dbgop == 1 || dbgop == 3) { dbg_name = dbgop == 1 ? "DBGA" : "DBGAH"; dbg_appd = dbgop == 1 ? ".H" : ""; actual = val >> 16; } else illegal_instruction (cpu); TRACE_INSN (cpu, "%s (%s%s, 0x%x);", dbg_name, reg_name, dbg_appd, expected); if (actual != expected) { sim_io_printf (sd, "FAIL at %#x: %s (%s%s, 0x%04x), actual value %#x\n", pc, dbg_name, reg_name, dbg_appd, expected, actual); cec_exception (cpu, VEC_SIM_DBGA); SET_DREG (0, 1); } } static bu32 _interp_insn_bfin (SIM_CPU *cpu, bu32 pc) { bu32 insn_len; bu16 iw0, iw1; BFIN_CPU_STATE.multi_pc = pc; iw0 = IFETCH (pc); if ((iw0 & 0xc000) != 0xc000) { /* 16-bit opcode. */ insn_len = 2; if (INSN_LEN == 0) INSN_LEN = insn_len; TRACE_EXTRACT (cpu, "%s: iw0:%#x", __func__, iw0); if ((iw0 & 0xFF00) == 0x0000) decode_ProgCtrl_0 (cpu, iw0, pc); else if ((iw0 & 0xFFC0) == 0x0240) decode_CaCTRL_0 (cpu, iw0); else if ((iw0 & 0xFF80) == 0x0100) decode_PushPopReg_0 (cpu, iw0); else if ((iw0 & 0xFE00) == 0x0400) decode_PushPopMultiple_0 (cpu, iw0); else if ((iw0 & 0xFE00) == 0x0600) decode_ccMV_0 (cpu, iw0); else if ((iw0 & 0xF800) == 0x0800) decode_CCflag_0 (cpu, iw0); else if ((iw0 & 0xFFE0) == 0x0200) decode_CC2dreg_0 (cpu, iw0); else if ((iw0 & 0xFF00) == 0x0300) decode_CC2stat_0 (cpu, iw0); else if ((iw0 & 0xF000) == 0x1000) decode_BRCC_0 (cpu, iw0, pc); else if ((iw0 & 0xF000) == 0x2000) decode_UJUMP_0 (cpu, iw0, pc); else if ((iw0 & 0xF000) == 0x3000) decode_REGMV_0 (cpu, iw0); else if ((iw0 & 0xFC00) == 0x4000) decode_ALU2op_0 (cpu, iw0); else if ((iw0 & 0xFE00) == 0x4400) decode_PTR2op_0 (cpu, iw0); else if ((iw0 & 0xF800) == 0x4800) decode_LOGI2op_0 (cpu, iw0); else if ((iw0 & 0xF000) == 0x5000) decode_COMP3op_0 (cpu, iw0); else if ((iw0 & 0xF800) == 0x6000) decode_COMPI2opD_0 (cpu, iw0); else if ((iw0 & 0xF800) == 0x6800) decode_COMPI2opP_0 (cpu, iw0); else if ((iw0 & 0xF000) == 0x8000) decode_LDSTpmod_0 (cpu, iw0); else if ((iw0 & 0xFF60) == 0x9E60) decode_dagMODim_0 (cpu, iw0); else if ((iw0 & 0xFFF0) == 0x9F60) decode_dagMODik_0 (cpu, iw0); else if ((iw0 & 0xFC00) == 0x9C00) decode_dspLDST_0 (cpu, iw0); else if ((iw0 & 0xF000) == 0x9000) decode_LDST_0 (cpu, iw0); else if ((iw0 & 0xFC00) == 0xB800) decode_LDSTiiFP_0 (cpu, iw0); else if ((iw0 & 0xE000) == 0xA000) decode_LDSTii_0 (cpu, iw0); else { TRACE_EXTRACT (cpu, "%s: no matching 16-bit pattern", __func__); illegal_instruction (cpu); } return insn_len; } /* Grab the next 16 bits to determine if it's a 32-bit or 64-bit opcode. */ iw1 = IFETCH (pc + 2); if ((iw0 & BIT_MULTI_INS) && (iw0 & 0xe800) != 0xe800 /* not linkage */) { SIM_DESC sd = CPU_STATE (cpu); trace_prefix (sd, cpu, NULL_CIA, pc, TRACE_LINENUM_P (cpu), NULL, 0, "|| %#"PRIx64, sim_events_time (sd)); insn_len = 8; } else insn_len = 4; TRACE_EXTRACT (cpu, "%s: iw0:%#x iw1:%#x insn_len:%i", __func__, iw0, iw1, insn_len); /* Only cache on first run through (in case of parallel insns). */ if (INSN_LEN == 0) INSN_LEN = insn_len; if ((iw0 & 0xf7ff) == 0xc003 && (iw1 & 0xfe00) == 0x1800) { PROFILE_COUNT_INSN (cpu, pc, BFIN_INSN_dsp32mac); TRACE_INSN (cpu, "MNOP;"); } else if (((iw0 & 0xFF80) == 0xE080) && ((iw1 & 0x0C00) == 0x0000)) decode_LoopSetup_0 (cpu, iw0, iw1, pc); else if (((iw0 & 0xFF00) == 0xE100) && ((iw1 & 0x0000) == 0x0000)) decode_LDIMMhalf_0 (cpu, iw0, iw1); else if (((iw0 & 0xFE00) == 0xE200) && ((iw1 & 0x0000) == 0x0000)) decode_CALLa_0 (cpu, iw0, iw1, pc); else if (((iw0 & 0xFC00) == 0xE400) && ((iw1 & 0x0000) == 0x0000)) decode_LDSTidxI_0 (cpu, iw0, iw1); else if (((iw0 & 0xFFFE) == 0xE800) && ((iw1 & 0x0000) == 0x0000)) decode_linkage_0 (cpu, iw0, iw1); else if (((iw0 & 0xF600) == 0xC000) && ((iw1 & 0x0000) == 0x0000)) decode_dsp32mac_0 (cpu, iw0, iw1); else if (((iw0 & 0xF600) == 0xC200) && ((iw1 & 0x0000) == 0x0000)) decode_dsp32mult_0 (cpu, iw0, iw1); else if (((iw0 & 0xF7C0) == 0xC400) && ((iw1 & 0x0000) == 0x0000)) decode_dsp32alu_0 (cpu, iw0, iw1); else if (((iw0 & 0xF7E0) == 0xC600) && ((iw1 & 0x01C0) == 0x0000)) decode_dsp32shift_0 (cpu, iw0, iw1); else if (((iw0 & 0xF7E0) == 0xC680) && ((iw1 & 0x0000) == 0x0000)) decode_dsp32shiftimm_0 (cpu, iw0, iw1); else if ((iw0 & 0xFF00) == 0xF800) decode_psedoDEBUG_0 (cpu, iw0), insn_len = 2; else if ((iw0 & 0xFF00) == 0xF900) decode_psedoOChar_0 (cpu, iw0), insn_len = 2; else if (((iw0 & 0xFF00) == 0xF000) && ((iw1 & 0x0000) == 0x0000)) decode_psedodbg_assert_0 (cpu, iw0, iw1, pc); else { TRACE_EXTRACT (cpu, "%s: no matching 32-bit pattern", __func__); illegal_instruction (cpu); } return insn_len; } bu32 interp_insn_bfin (SIM_CPU *cpu, bu32 pc) { int i; bu32 insn_len; BFIN_CPU_STATE.n_stores = 0; DIS_ALGN_EXPT &= ~1; CYCLE_DELAY = 1; INSN_LEN = 0; insn_len = _interp_insn_bfin (cpu, pc); /* Proper display of multiple issue instructions. */ if (insn_len == 8) { _interp_insn_bfin (cpu, pc + 4); _interp_insn_bfin (cpu, pc + 6); } for (i = 0; i < BFIN_CPU_STATE.n_stores; i++) { bu32 *addr = BFIN_CPU_STATE.stores[i].addr; *addr = BFIN_CPU_STATE.stores[i].val; TRACE_REGISTER (cpu, "dequeuing write %s = %#x", get_store_name (cpu, addr), *addr); } cycles_inc (cpu, CYCLE_DELAY); /* Set back to zero in case a pending CEC event occurs after this this insn. */ INSN_LEN = 0; return insn_len; }