/* rx.c --- opcode semantics for stand-alone RX simulator. Copyright (C) 2008, 2009 Free Software Foundation, Inc. Contributed by Red Hat, Inc. This file is part of the GNU 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 #include #include #include #include "opcode/rx.h" #include "cpu.h" #include "mem.h" #include "syscalls.h" #include "fpu.h" #include "err.h" #define tprintf if (trace) printf jmp_buf decode_jmp_buf; unsigned int rx_cycles = 0; static int size2bytes[] = { 4, 1, 1, 1, 2, 2, 2, 3, 4 }; typedef struct { unsigned long dpc; } RX_Data; #define rx_abort() _rx_abort(__FILE__, __LINE__) static void _rx_abort (const char *file, int line) { if (strrchr (file, '/')) file = strrchr (file, '/') + 1; fprintf(stderr, "abort at %s:%d\n", file, line); abort(); } static int rx_get_byte (void *vdata) { int saved_trace = trace; unsigned char rv; if (trace == 1) trace = 0; RX_Data *rx_data = (RX_Data *)vdata; if (rx_big_endian) /* See load.c for an explanation of this. */ rv = mem_get_pc (rx_data->dpc ^ 3); else rv = mem_get_pc (rx_data->dpc); rx_data->dpc ++; trace = saved_trace; return rv; } static int get_op (RX_Opcode_Decoded *rd, int i) { RX_Opcode_Operand *o = rd->op + i; int addr, rv = 0; switch (o->type) { case RX_Operand_None: rx_abort (); case RX_Operand_Immediate: /* #addend */ return o->addend; case RX_Operand_Register: /* Rn */ rv = get_reg (o->reg); break; case RX_Operand_Predec: /* [-Rn] */ put_reg (o->reg, get_reg (o->reg) - size2bytes[o->size]); /* fall through */ case RX_Operand_Postinc: /* [Rn+] */ case RX_Operand_Indirect: /* [Rn + addend] */ addr = get_reg (o->reg) + o->addend; switch (o->size) { case RX_AnySize: rx_abort (); case RX_Byte: /* undefined extension */ case RX_UByte: case RX_SByte: rv = mem_get_qi (addr); break; case RX_Word: /* undefined extension */ case RX_UWord: case RX_SWord: rv = mem_get_hi (addr); break; case RX_3Byte: rv = mem_get_psi (addr); break; case RX_Long: rv = mem_get_si (addr); break; } if (o->type == RX_Operand_Postinc) put_reg (o->reg, get_reg (o->reg) + size2bytes[o->size]); break; case RX_Operand_Condition: /* eq, gtu, etc */ return condition_true (o->reg); case RX_Operand_Flag: /* [UIOSZC] */ return (regs.r_psw & (1 << o->reg)) ? 1 : 0; } /* if we've gotten here, we need to clip/extend the value according to the size. */ switch (o->size) { case RX_AnySize: rx_abort (); case RX_Byte: /* undefined extension */ rv |= 0xdeadbe00; /* keep them honest */ break; case RX_UByte: rv &= 0xff; break; case RX_SByte: rv = sign_ext (rv, 8); break; case RX_Word: /* undefined extension */ rv |= 0xdead0000; /* keep them honest */ break; case RX_UWord: rv &= 0xffff; break; case RX_SWord: rv = sign_ext (rv, 16); break; case RX_3Byte: rv &= 0xffffff; break; case RX_Long: break; } return rv; } static void put_op (RX_Opcode_Decoded *rd, int i, int v) { RX_Opcode_Operand *o = rd->op + i; int addr; switch (o->size) { case RX_AnySize: if (o->type != RX_Operand_Register) rx_abort (); break; case RX_Byte: /* undefined extension */ v |= 0xdeadbe00; /* keep them honest */ break; case RX_UByte: v &= 0xff; break; case RX_SByte: v = sign_ext (v, 8); break; case RX_Word: /* undefined extension */ v |= 0xdead0000; /* keep them honest */ break; case RX_UWord: v &= 0xffff; break; case RX_SWord: v = sign_ext (v, 16); break; case RX_3Byte: v &= 0xffffff; break; case RX_Long: break; } switch (o->type) { case RX_Operand_None: /* Opcodes like TST and CMP use this. */ break; case RX_Operand_Immediate: /* #addend */ case RX_Operand_Condition: /* eq, gtu, etc */ rx_abort (); case RX_Operand_Register: /* Rn */ put_reg (o->reg, v); break; case RX_Operand_Predec: /* [-Rn] */ put_reg (o->reg, get_reg (o->reg) - size2bytes[o->size]); /* fall through */ case RX_Operand_Postinc: /* [Rn+] */ case RX_Operand_Indirect: /* [Rn + addend] */ addr = get_reg (o->reg) + o->addend; switch (o->size) { case RX_AnySize: rx_abort (); case RX_Byte: /* undefined extension */ case RX_UByte: case RX_SByte: mem_put_qi (addr, v); break; case RX_Word: /* undefined extension */ case RX_UWord: case RX_SWord: mem_put_hi (addr, v); break; case RX_3Byte: mem_put_psi (addr, v); break; case RX_Long: mem_put_si (addr, v); break; } if (o->type == RX_Operand_Postinc) put_reg (o->reg, get_reg (o->reg) + size2bytes[o->size]); break; case RX_Operand_Flag: /* [UIOSZC] */ if (v) regs.r_psw |= (1 << o->reg); else regs.r_psw &= ~(1 << o->reg); break; } } #define PD(x) put_op (&opcode, 0, x) #define PS(x) put_op (&opcode, 1, x) #define PS2(x) put_op (&opcode, 2, x) #define GD() get_op (&opcode, 0) #define GS() get_op (&opcode, 1) #define GS2() get_op (&opcode, 2) #define DSZ() size2bytes[opcode.op[0].size] #define SSZ() size2bytes[opcode.op[0].size] #define S2SZ() size2bytes[opcode.op[0].size] /* "Universal" sources. */ #define US1() ((opcode.op[2].type == RX_Operand_None) ? GD() : GS()) #define US2() ((opcode.op[2].type == RX_Operand_None) ? GS() : GS2()) static void push(int val) { int rsp = get_reg (sp); rsp -= 4; put_reg (sp, rsp); mem_put_si (rsp, val); } /* Just like the above, but tag the memory as "pushed pc" so if anyone tries to write to it, it will cause an error. */ static void pushpc(int val) { int rsp = get_reg (sp); rsp -= 4; put_reg (sp, rsp); mem_put_si (rsp, val); mem_set_content_range (rsp, rsp+3, MC_PUSHED_PC); } static int pop() { int rv; int rsp = get_reg (sp); rv = mem_get_si (rsp); rsp += 4; put_reg (sp, rsp); return rv; } static int poppc() { int rv; int rsp = get_reg (sp); if (mem_get_content_type (rsp) != MC_PUSHED_PC) execution_error (SIM_ERR_CORRUPT_STACK, rsp); rv = mem_get_si (rsp); mem_set_content_range (rsp, rsp+3, MC_UNINIT); rsp += 4; put_reg (sp, rsp); return rv; } #define MATH_OP(vop,c) \ { \ uma = US1(); \ umb = US2(); \ ll = (unsigned long long) uma vop (unsigned long long) umb vop c; \ tprintf ("0x%x " #vop " 0x%x " #vop " 0x%x = 0x%llx\n", uma, umb, c, ll); \ ma = sign_ext (uma, DSZ() * 8); \ mb = sign_ext (umb, DSZ() * 8); \ sll = (long long) ma vop (long long) mb vop c; \ tprintf ("%d " #vop " %d " #vop " %d = %lld\n", ma, mb, c, sll); \ set_oszc (sll, DSZ(), (long long) ll > ((1 vop 1) ? (long long) b2mask[DSZ()] : (long long) -1)); \ PD (sll); \ } #define LOGIC_OP(vop) \ { \ ma = US1(); \ mb = US2(); \ v = ma vop mb; \ tprintf("0x%x " #vop " 0x%x = 0x%x\n", ma, mb, v); \ set_sz (v, DSZ()); \ PD(v); \ } #define SHIFT_OP(val, type, count, OP, carry_mask) \ { \ int i, c=0; \ val = (type)US1(); \ count = US2(); \ tprintf("%lld " #OP " %d\n", val, count); \ for (i = 0; i < count; i ++) \ { \ c = val & carry_mask; \ val OP 1; \ } \ if (count) \ set_oszc (val, 4, c); \ PD (val); \ } typedef union { int i; float f; } FloatInt; static inline int float2int (float f) { FloatInt fi; fi.f = f; return fi.i; } static inline float int2float (int i) { FloatInt fi; fi.i = i; return fi.f; } static int fop_fadd (fp_t s1, fp_t s2, fp_t *d) { *d = rxfp_add (s1, s2); return 1; } static int fop_fmul (fp_t s1, fp_t s2, fp_t *d) { *d = rxfp_mul (s1, s2); return 1; } static int fop_fdiv (fp_t s1, fp_t s2, fp_t *d) { *d = rxfp_div (s1, s2); return 1; } static int fop_fsub (fp_t s1, fp_t s2, fp_t *d) { *d = rxfp_sub (s1, s2); return 1; } #define FPPENDING() (regs.r_fpsw & (FPSWBITS_CE | (FPSWBITS_FMASK & (regs.r_fpsw << FPSW_EFSH)))) #define FPCLEAR() regs.r_fpsw &= FPSWBITS_CLEAR #define FPCHECK() \ if (FPPENDING()) \ return do_fp_exception (opcode_pc) #define FLOAT_OP(func) \ { \ int do_store; \ fp_t fa, fb, fc; \ FPCLEAR(); \ fa = GD (); \ fb = GS (); \ do_store = fop_##func (fa, fb, &fc); \ tprintf("%g " #func " %g = %g %08x\n", int2float(fa), int2float(fb), int2float(fc), fc); \ FPCHECK(); \ if (do_store) \ PD (fc); \ mb = 0; \ if ((fc & 0x80000000UL) != 0) \ mb |= FLAGBIT_S; \ if ((fc & 0x7fffffffUL) == 0) \ mb |= FLAGBIT_Z; \ set_flags (FLAGBIT_S | FLAGBIT_Z, mb); \ } #define carry (FLAG_C ? 1 : 0) static struct { unsigned long vaddr; const char *str; int signal; } exception_info[] = { { 0xFFFFFFD0UL, "priviledged opcode", SIGILL }, { 0xFFFFFFD4UL, "access violation", SIGSEGV }, { 0xFFFFFFDCUL, "undefined opcode", SIGILL }, { 0xFFFFFFE4UL, "floating point", SIGFPE } }; #define EX_PRIVILEDGED 0 #define EX_ACCESS 1 #define EX_UNDEFINED 2 #define EX_FLOATING 3 #define EXCEPTION(n) \ return generate_exception (n, opcode_pc) #define PRIVILEDGED() \ if (FLAG_PM) \ EXCEPTION (EX_PRIVILEDGED) static int generate_exception (unsigned long type, SI opcode_pc) { SI old_psw, old_pc, new_pc; new_pc = mem_get_si (exception_info[type].vaddr); /* 0x00020000 is the value used to initialise the known exception vectors (see rx.ld), but it is a reserved area of memory so do not try to access it, and if the value has not been changed by the program then the vector has not been installed. */ if (new_pc == 0 || new_pc == 0x00020000) { if (rx_in_gdb) return RX_MAKE_STOPPED (exception_info[type].signal); fprintf(stderr, "Unhandled %s exception at pc = %#lx\n", exception_info[type].str, (unsigned long) opcode_pc); if (type == EX_FLOATING) { int mask = FPPENDING (); fprintf (stderr, "Pending FP exceptions:"); if (mask & FPSWBITS_FV) fprintf(stderr, " Invalid"); if (mask & FPSWBITS_FO) fprintf(stderr, " Overflow"); if (mask & FPSWBITS_FZ) fprintf(stderr, " Division-by-zero"); if (mask & FPSWBITS_FU) fprintf(stderr, " Underflow"); if (mask & FPSWBITS_FX) fprintf(stderr, " Inexact"); if (mask & FPSWBITS_CE) fprintf(stderr, " Unimplemented"); fprintf(stderr, "\n"); } return RX_MAKE_EXITED (1); } tprintf ("Triggering %s exception\n", exception_info[type].str); old_psw = regs.r_psw; regs.r_psw &= ~ (FLAGBIT_I | FLAGBIT_U | FLAGBIT_PM); old_pc = opcode_pc; regs.r_pc = new_pc; pushpc (old_psw); pushpc (old_pc); return RX_MAKE_STEPPED (); } void generate_access_exception (void) { int rv; rv = generate_exception (EX_ACCESS, regs.r_pc); if (RX_EXITED (rv)) longjmp (decode_jmp_buf, rv); } static int do_fp_exception (unsigned long opcode_pc) { while (FPPENDING()) EXCEPTION (EX_FLOATING); return RX_MAKE_STEPPED (); } int decode_opcode () { unsigned int uma=0, umb=0; int ma=0, mb=0; int opcode_size, v; unsigned long long ll; long long sll; unsigned long opcode_pc; RX_Data rx_data; RX_Opcode_Decoded opcode; int rv; if ((rv = setjmp (decode_jmp_buf))) return rv; rx_cycles ++; rx_data.dpc = opcode_pc = regs.r_pc; opcode_size = rx_decode_opcode (opcode_pc, &opcode, rx_get_byte, &rx_data); regs.r_pc += opcode_size; rx_flagmask = opcode.flags_s; rx_flagand = ~(int)opcode.flags_0; rx_flagor = opcode.flags_1; switch (opcode.id) { case RXO_abs: sll = GS (); tprintf("|%lld| = ", sll); if (sll < 0) sll = -sll; tprintf("%lld\n", sll); PD (sll); set_osz (sll, 4); break; case RXO_adc: MATH_OP (+,carry); break; case RXO_add: MATH_OP (+,0); break; case RXO_and: LOGIC_OP (&); break; case RXO_bclr: ma = GD (); mb = GS (); if (opcode.op[0].type == RX_Operand_Register) mb &= 0x1f; else mb &= 0x07; ma &= ~(1 << mb); PD (ma); break; case RXO_bmcc: ma = GD (); mb = GS (); if (opcode.op[0].type == RX_Operand_Register) mb &= 0x1f; else mb &= 0x07; if (GS2 ()) ma |= (1 << mb); else ma &= ~(1 << mb); PD (ma); break; case RXO_bnot: ma = GD (); mb = GS (); if (opcode.op[0].type == RX_Operand_Register) mb &= 0x1f; else mb &= 0x07; ma ^= (1 << mb); PD (ma); break; case RXO_branch: if (GS()) regs.r_pc = GD(); break; case RXO_branchrel: if (GS()) regs.r_pc += GD(); break; case RXO_brk: { int old_psw = regs.r_psw; if (rx_in_gdb) return RX_MAKE_HIT_BREAK (); if (regs.r_intb == 0) { tprintf("BREAK hit, no vector table.\n"); return RX_MAKE_EXITED(1); } regs.r_psw &= ~(FLAGBIT_I | FLAGBIT_U | FLAGBIT_PM); pushpc (old_psw); pushpc (regs.r_pc); regs.r_pc = mem_get_si (regs.r_intb); } break; case RXO_bset: ma = GD (); mb = GS (); if (opcode.op[0].type == RX_Operand_Register) mb &= 0x1f; else mb &= 0x07; ma |= (1 << mb); PD (ma); break; case RXO_btst: ma = GS (); mb = GS2 (); if (opcode.op[0].type == RX_Operand_Register) mb &= 0x1f; else mb &= 0x07; umb = ma & (1 << mb); set_zc (! umb, umb); break; case RXO_clrpsw: v = 1 << opcode.op[0].reg; if (FLAG_PM && (v == FLAGBIT_I || v == FLAGBIT_U)) break; regs.r_psw &= ~v; break; case RXO_div: /* d = d / s */ ma = GS(); mb = GD(); tprintf("%d / %d = ", mb, ma); if (ma == 0 || (ma == -1 && (unsigned int) mb == 0x80000000)) { tprintf("#NAN\n"); set_flags (FLAGBIT_O, FLAGBIT_O); } else { v = mb/ma; tprintf("%d\n", v); set_flags (FLAGBIT_O, 0); PD (v); } break; case RXO_divu: /* d = d / s */ uma = GS(); umb = GD(); tprintf("%u / %u = ", umb, uma); if (uma == 0) { tprintf("#NAN\n"); set_flags (FLAGBIT_O, FLAGBIT_O); } else { v = umb / uma; tprintf("%u\n", v); set_flags (FLAGBIT_O, 0); PD (v); } break; case RXO_ediv: ma = GS(); mb = GD(); tprintf("%d / %d = ", mb, ma); if (ma == 0 || (ma == -1 && (unsigned int) mb == 0x80000000)) { tprintf("#NAN\n"); set_flags (FLAGBIT_O, FLAGBIT_O); } else { v = mb/ma; mb = mb%ma; tprintf("%d, rem %d\n", v, mb); set_flags (FLAGBIT_O, 0); PD (v); opcode.op[0].reg ++; PD (mb); } break; case RXO_edivu: uma = GS(); umb = GD(); tprintf("%u / %u = ", umb, uma); if (uma == 0) { tprintf("#NAN\n"); set_flags (FLAGBIT_O, FLAGBIT_O); } else { v = umb/uma; umb = umb%uma; tprintf("%u, rem %u\n", v, umb); set_flags (FLAGBIT_O, 0); PD (v); opcode.op[0].reg ++; PD (umb); } break; case RXO_emul: ma = GD (); mb = GS (); sll = (long long)ma * (long long)mb; tprintf("%d * %d = %lld\n", ma, mb, sll); PD (sll); opcode.op[0].reg ++; PD (sll >> 32); break; case RXO_emulu: uma = GD (); umb = GS (); ll = (long long)uma * (long long)umb; tprintf("%#x * %#x = %#llx\n", uma, umb, ll); PD (ll); opcode.op[0].reg ++; PD (ll >> 32); break; case RXO_fadd: FLOAT_OP (fadd); break; case RXO_fcmp: ma = GD(); mb = GS(); FPCLEAR (); rxfp_cmp (ma, mb); FPCHECK (); break; case RXO_fdiv: FLOAT_OP (fdiv); break; case RXO_fmul: FLOAT_OP (fmul); break; case RXO_rtfi: PRIVILEDGED (); regs.r_psw = regs.r_bpsw; regs.r_pc = regs.r_bpc; break; case RXO_fsub: FLOAT_OP (fsub); break; case RXO_ftoi: ma = GS (); FPCLEAR (); mb = rxfp_ftoi (ma, FPRM_ZERO); FPCHECK (); PD (mb); tprintf("(int) %g = %d\n", int2float(ma), mb); set_sz (mb, 4); break; case RXO_int: v = GS (); if (v == 255) { return rx_syscall (regs.r[5]); } else { int old_psw = regs.r_psw; regs.r_psw &= ~(FLAGBIT_I | FLAGBIT_U | FLAGBIT_PM); pushpc (old_psw); pushpc (regs.r_pc); regs.r_pc = mem_get_si (regs.r_intb + 4 * v); } break; case RXO_itof: ma = GS (); FPCLEAR (); mb = rxfp_itof (ma, regs.r_fpsw); FPCHECK (); tprintf("(float) %d = %x\n", ma, mb); PD (mb); set_sz (ma, 4); break; case RXO_jsr: case RXO_jsrrel: v = GD (); pushpc (get_reg (pc)); if (opcode.id == RXO_jsrrel) v += regs.r_pc; put_reg (pc, v); break; case RXO_machi: ll = (long long)(signed short)(GS() >> 16) * (long long)(signed short)(GS2 () >> 16); ll <<= 16; put_reg64 (acc64, ll + regs.r_acc); break; case RXO_maclo: ll = (long long)(signed short)(GS()) * (long long)(signed short)(GS2 ()); ll <<= 16; put_reg64 (acc64, ll + regs.r_acc); break; case RXO_max: ma = GD(); mb = GS(); if (ma > mb) PD (ma); else PD (mb); break; case RXO_min: ma = GD(); mb = GS(); if (ma < mb) PD (ma); else PD (mb); break; case RXO_mov: v = GS (); if (opcode.op[0].type == RX_Operand_Register && opcode.op[0].reg == 16 /* PSW */) { /* Special case, LDC and POPC can't ever modify PM. */ int pm = regs.r_psw & FLAGBIT_PM; v &= ~ FLAGBIT_PM; v |= pm; if (pm) { v &= ~ (FLAGBIT_I | FLAGBIT_U | FLAGBITS_IPL); v |= pm; } } if (FLAG_PM) { /* various things can't be changed in user mode. */ if (opcode.op[0].type == RX_Operand_Register) if (opcode.op[0].reg == 32) { v &= ~ (FLAGBIT_I | FLAGBIT_U | FLAGBITS_IPL); v |= regs.r_psw & (FLAGBIT_I | FLAGBIT_U | FLAGBITS_IPL); } if (opcode.op[0].reg == 34 /* ISP */ || opcode.op[0].reg == 37 /* BPSW */ || opcode.op[0].reg == 39 /* INTB */ || opcode.op[0].reg == 38 /* VCT */) /* These are ignored. */ break; } PD (v); set_sz (v, DSZ()); break; case RXO_movbi: /* We cheat to save on code duplication. */ regs.r_temp = (get_reg (opcode.op[1].reg) * size2bytes[opcode.size] + get_reg (opcode.op[2].reg)); opcode.op[1].reg = r_temp_idx; opcode.op[1].type = RX_Operand_Indirect; opcode.op[1].addend = 0; PD (GS ()); break; case RXO_movbir: /* We cheat to save on code duplication. */ regs.r_temp = (get_reg (opcode.op[1].reg) * size2bytes[opcode.size] + get_reg (opcode.op[2].reg)); opcode.op[1].reg = r_temp_idx; opcode.op[1].type = RX_Operand_Indirect; opcode.op[1].addend = 0; PS (GD ()); break; case RXO_mul: ll = (unsigned long long) US1() * (unsigned long long) US2(); PD(ll); break; case RXO_mulhi: ll = (long long)(signed short)(GS() >> 16) * (long long)(signed short)(GS2 () >> 16); ll <<= 16; put_reg64 (acc64, ll); break; case RXO_mullo: ll = (long long)(signed short)(GS()) * (long long)(signed short)(GS2 ()); ll <<= 16; put_reg64 (acc64, ll); break; case RXO_mvfachi: PD (get_reg (acchi)); break; case RXO_mvfaclo: PD (get_reg (acclo)); break; case RXO_mvfacmi: PD (get_reg (accmi)); break; case RXO_mvtachi: put_reg (acchi, GS ()); break; case RXO_mvtaclo: put_reg (acclo, GS ()); break; case RXO_mvtipl: regs.r_psw &= ~ FLAGBITS_IPL; regs.r_psw |= (GS () << FLAGSHIFT_IPL) & FLAGBITS_IPL; break; case RXO_nop: break; case RXO_or: LOGIC_OP (|); break; case RXO_popm: /* POPM cannot pop R0 (sp). */ if (opcode.op[1].reg == 0 || opcode.op[2].reg == 0) EXCEPTION (EX_UNDEFINED); if (opcode.op[1].reg >= opcode.op[2].reg) { regs.r_pc = opcode_pc; return RX_MAKE_STOPPED (SIGILL); } for (v = opcode.op[1].reg; v <= opcode.op[2].reg; v++) put_reg (v, pop ()); break; case RXO_pusha: push (get_reg (opcode.op[1].reg) + opcode.op[1].addend); break; case RXO_pushm: /* PUSHM cannot push R0 (sp). */ if (opcode.op[1].reg == 0 || opcode.op[2].reg == 0) EXCEPTION (EX_UNDEFINED); if (opcode.op[1].reg >= opcode.op[2].reg) { regs.r_pc = opcode_pc; return RX_MAKE_STOPPED (SIGILL); } for (v = opcode.op[2].reg; v >= opcode.op[1].reg; v--) push (get_reg (v)); break; case RXO_racw: ll = get_reg64 (acc64) << GS (); ll += 0x80000000ULL; if ((signed long long)ll > (signed long long)0x00007fff00000000ULL) ll = 0x00007fff00000000ULL; else if ((signed long long)ll < (signed long long)0xffff800000000000ULL) ll = 0xffff800000000000ULL; else ll &= 0xffffffff00000000ULL; put_reg64 (acc64, ll); break; case RXO_rte: PRIVILEDGED (); regs.r_pc = poppc (); regs.r_psw = poppc (); if (FLAG_PM) regs.r_psw |= FLAGBIT_U; break; case RXO_revl: uma = GS (); umb = (((uma >> 24) & 0xff) | ((uma >> 8) & 0xff00) | ((uma << 8) & 0xff0000) | ((uma << 24) & 0xff000000UL)); PD (umb); break; case RXO_revw: uma = GS (); umb = (((uma >> 8) & 0x00ff00ff) | ((uma << 8) & 0xff00ff00UL)); PD (umb); break; case RXO_rmpa: while (regs.r[3] != 0) { long long tmp; switch (opcode.size) { case RX_Long: ma = mem_get_si (regs.r[1]); mb = mem_get_si (regs.r[2]); regs.r[1] += 4; regs.r[2] += 4; break; case RX_Word: ma = sign_ext (mem_get_hi (regs.r[1]), 16); mb = sign_ext (mem_get_hi (regs.r[2]), 16); regs.r[1] += 2; regs.r[2] += 2; break; case RX_Byte: ma = sign_ext (mem_get_qi (regs.r[1]), 8); mb = sign_ext (mem_get_qi (regs.r[2]), 8); regs.r[1] += 1; regs.r[2] += 1; break; default: abort (); } /* We do the multiply as a signed value. */ sll = (long long)ma * (long long)mb; tprintf(" %016llx = %d * %d\n", sll, ma, mb); /* but we do the sum as unsigned, while sign extending the operands. */ tmp = regs.r[4] + (sll & 0xffffffffUL); regs.r[4] = tmp & 0xffffffffUL; tmp >>= 32; sll >>= 32; tmp += regs.r[5] + (sll & 0xffffffffUL); regs.r[5] = tmp & 0xffffffffUL; tmp >>= 32; sll >>= 32; tmp += regs.r[6] + (sll & 0xffffffffUL); regs.r[6] = tmp & 0xffffffffUL; tprintf("%08lx\033[36m%08lx\033[0m%08lx\n", (unsigned long) regs.r[6], (unsigned long) regs.r[5], (unsigned long) regs.r[4]); regs.r[3] --; } if (regs.r[6] & 0x00008000) regs.r[6] |= 0xffff0000UL; else regs.r[6] &= 0x0000ffff; ma = (regs.r[6] & 0x80000000UL) ? FLAGBIT_S : 0; if (regs.r[6] != 0 && regs.r[6] != 0xffffffffUL) set_flags (FLAGBIT_O|FLAGBIT_S, ma | FLAGBIT_O); else set_flags (FLAGBIT_O|FLAGBIT_S, ma); break; case RXO_rolc: v = GD (); ma = v & 0x80000000UL; v <<= 1; v |= carry; set_szc (v, 4, ma); PD (v); break; case RXO_rorc: uma = GD (); mb = uma & 1; uma >>= 1; uma |= (carry ? 0x80000000UL : 0); set_szc (uma, 4, mb); PD (uma); break; case RXO_rotl: mb = GS (); uma = GD (); if (mb) { uma = (uma << mb) | (uma >> (32-mb)); mb = uma & 1; } set_szc (uma, 4, mb); PD (uma); break; case RXO_rotr: mb = GS (); uma = GD (); if (mb) { uma = (uma >> mb) | (uma << (32-mb)); mb = uma & 0x80000000; } set_szc (uma, 4, mb); PD (uma); break; case RXO_round: ma = GS (); FPCLEAR (); mb = rxfp_ftoi (ma, regs.r_fpsw); FPCHECK (); PD (mb); tprintf("(int) %g = %d\n", int2float(ma), mb); set_sz (mb, 4); break; case RXO_rts: regs.r_pc = poppc (); break; case RXO_rtsd: if (opcode.op[2].type == RX_Operand_Register) { int i; /* RTSD cannot pop R0 (sp). */ put_reg (0, get_reg (0) + GS() - (opcode.op[0].reg-opcode.op[2].reg+1)*4); if (opcode.op[2].reg == 0) EXCEPTION (EX_UNDEFINED); for (i = opcode.op[2].reg; i <= opcode.op[0].reg; i ++) put_reg (i, pop ()); } else put_reg (0, get_reg (0) + GS()); put_reg (pc, poppc ()); break; case RXO_sat: if (FLAG_O && FLAG_S) PD (0x7fffffffUL); else if (FLAG_O && ! FLAG_S) PD (0x80000000UL); break; case RXO_sbb: MATH_OP (-, ! carry); break; case RXO_sccnd: if (GS()) PD (1); else PD (0); break; case RXO_scmpu: while (regs.r[3] != 0) { uma = mem_get_qi (regs.r[1] ++); umb = mem_get_qi (regs.r[2] ++); regs.r[3] --; if (uma != umb || uma == 0) break; } if (uma == umb) set_zc (1, 1); else set_zc (0, ((int)uma - (int)umb) >= 0); break; case RXO_setpsw: v = 1 << opcode.op[0].reg; if (FLAG_PM && (v == FLAGBIT_I || v == FLAGBIT_U)) break; regs.r_psw |= v; break; case RXO_smovb: while (regs.r[3]) { uma = mem_get_qi (regs.r[2] --); mem_put_qi (regs.r[1]--, uma); regs.r[3] --; } break; case RXO_smovf: while (regs.r[3]) { uma = mem_get_qi (regs.r[2] ++); mem_put_qi (regs.r[1]++, uma); regs.r[3] --; } break; case RXO_smovu: while (regs.r[3] != 0) { uma = mem_get_qi (regs.r[2] ++); mem_put_qi (regs.r[1]++, uma); regs.r[3] --; if (uma == 0) break; } break; case RXO_shar: /* d = ma >> mb */ SHIFT_OP (sll, int, mb, >>=, 1); break; case RXO_shll: /* d = ma << mb */ SHIFT_OP (ll, int, mb, <<=, 0x80000000UL); break; case RXO_shlr: /* d = ma >> mb */ SHIFT_OP (ll, unsigned int, mb, >>=, 1); break; case RXO_sstr: switch (opcode.size) { case RX_Long: while (regs.r[3] != 0) { mem_put_si (regs.r[1], regs.r[2]); regs.r[1] += 4; regs.r[3] --; } break; case RX_Word: while (regs.r[3] != 0) { mem_put_hi (regs.r[1], regs.r[2]); regs.r[1] += 2; regs.r[3] --; } break; case RX_Byte: while (regs.r[3] != 0) { mem_put_qi (regs.r[1], regs.r[2]); regs.r[1] ++; regs.r[3] --; } break; default: abort (); } break; case RXO_stcc: if (GS2()) PD (GS ()); break; case RXO_stop: PRIVILEDGED (); regs.r_psw |= FLAGBIT_I; return RX_MAKE_STOPPED(0); case RXO_sub: MATH_OP (-, 0); break; case RXO_suntil: if (regs.r[3] == 0) break; switch (opcode.size) { case RX_Long: uma = get_reg (2); while (regs.r[3] != 0) { regs.r[3] --; umb = mem_get_si (get_reg (1)); regs.r[1] += 4; if (umb == uma) break; } break; case RX_Word: uma = get_reg (2) & 0xffff; while (regs.r[3] != 0) { regs.r[3] --; umb = mem_get_hi (get_reg (1)); regs.r[1] += 2; if (umb == uma) break; } break; case RX_Byte: uma = get_reg (2) & 0xff; while (regs.r[3] != 0) { regs.r[3] --; umb = mem_get_qi (regs.r[1]); regs.r[1] += 1; if (umb == uma) break; } break; default: abort(); } if (uma == umb) set_zc (1, 1); else set_zc (0, ((int)uma - (int)umb) >= 0); break; case RXO_swhile: if (regs.r[3] == 0) break; switch (opcode.size) { case RX_Long: uma = get_reg (2); while (regs.r[3] != 0) { regs.r[3] --; umb = mem_get_si (get_reg (1)); if (umb != uma) break; regs.r[1] += 4; } break; case RX_Word: uma = get_reg (2) & 0xffff; while (regs.r[3] != 0) { regs.r[3] --; umb = mem_get_hi (get_reg (1)); if (umb != uma) break; regs.r[1] += 2; } break; case RX_Byte: uma = get_reg (2) & 0xff; while (regs.r[3] != 0) { regs.r[3] --; umb = mem_get_qi (regs.r[1]); if (umb != uma) break; regs.r[1] += 1; } break; default: abort(); } if (uma == umb) set_zc (1, 1); else set_zc (0, ((int)uma - (int)umb) >= 0); break; case RXO_wait: PRIVILEDGED (); regs.r_psw |= FLAGBIT_I; return RX_MAKE_STOPPED(0); case RXO_xchg: v = GS (); /* This is the memory operand, if any. */ PS (GD ()); /* and this may change the address register. */ PD (v); break; case RXO_xor: LOGIC_OP (^); break; default: EXCEPTION (EX_UNDEFINED); } return RX_MAKE_STEPPED (); }