diff options
Diffstat (limited to 'sim/rx/rx.c')
-rw-r--r-- | sim/rx/rx.c | 1444 |
1 files changed, 1444 insertions, 0 deletions
diff --git a/sim/rx/rx.c b/sim/rx/rx.c new file mode 100644 index 00000000000..888aa862cc7 --- /dev/null +++ b/sim/rx/rx.c @@ -0,0 +1,1444 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> + +#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 (); +} |