mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:54:41 +08:00
1d506c26d9
This commit is the result of the following actions: - Running gdb/copyright.py to update all of the copyright headers to include 2024, - Manually updating a few files the copyright.py script told me to update, these files had copyright headers embedded within the file, - Regenerating gdbsupport/Makefile.in to refresh it's copyright date, - Using grep to find other files that still mentioned 2023. If these files were updated last year from 2022 to 2023 then I've updated them this year to 2024. I'm sure I've probably missed some dates. Feel free to fix them up as you spot them.
2284 lines
46 KiB
C
2284 lines
46 KiB
C
/* rx.c --- opcode semantics for stand-alone RX simulator.
|
|
|
|
Copyright (C) 2008-2024 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/>. */
|
|
|
|
/* This must come before any other includes. */
|
|
#include "defs.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include "libiberty.h"
|
|
|
|
#include "opcode/rx.h"
|
|
#include "cpu.h"
|
|
#include "mem.h"
|
|
#include "syscalls.h"
|
|
#include "fpu.h"
|
|
#include "err.h"
|
|
#include "misc.h"
|
|
|
|
#ifdef WITH_PROFILE
|
|
static const char * const id_names[] = {
|
|
"RXO_unknown",
|
|
"RXO_mov", /* d = s (signed) */
|
|
"RXO_movbi", /* d = [s,s2] (signed) */
|
|
"RXO_movbir", /* [s,s2] = d (signed) */
|
|
"RXO_pushm", /* s..s2 */
|
|
"RXO_popm", /* s..s2 */
|
|
"RXO_xchg", /* s <-> d */
|
|
"RXO_stcc", /* d = s if cond(s2) */
|
|
"RXO_rtsd", /* rtsd, 1=imm, 2-0 = reg if reg type */
|
|
|
|
/* These are all either d OP= s or, if s2 is set, d = s OP s2. Note
|
|
that d may be "None". */
|
|
"RXO_and",
|
|
"RXO_or",
|
|
"RXO_xor",
|
|
"RXO_add",
|
|
"RXO_sub",
|
|
"RXO_mul",
|
|
"RXO_div",
|
|
"RXO_divu",
|
|
"RXO_shll",
|
|
"RXO_shar",
|
|
"RXO_shlr",
|
|
|
|
"RXO_adc", /* d = d + s + carry */
|
|
"RXO_sbb", /* d = d - s - ~carry */
|
|
"RXO_abs", /* d = |s| */
|
|
"RXO_max", /* d = max(d,s) */
|
|
"RXO_min", /* d = min(d,s) */
|
|
"RXO_emul", /* d:64 = d:32 * s */
|
|
"RXO_emulu", /* d:64 = d:32 * s (unsigned) */
|
|
|
|
"RXO_rolc", /* d <<= 1 through carry */
|
|
"RXO_rorc", /* d >>= 1 through carry*/
|
|
"RXO_rotl", /* d <<= #s without carry */
|
|
"RXO_rotr", /* d >>= #s without carry*/
|
|
"RXO_revw", /* d = revw(s) */
|
|
"RXO_revl", /* d = revl(s) */
|
|
"RXO_branch", /* pc = d if cond(s) */
|
|
"RXO_branchrel",/* pc += d if cond(s) */
|
|
"RXO_jsr", /* pc = d */
|
|
"RXO_jsrrel", /* pc += d */
|
|
"RXO_rts",
|
|
"RXO_nop",
|
|
"RXO_nop2",
|
|
"RXO_nop3",
|
|
"RXO_nop4",
|
|
"RXO_nop5",
|
|
"RXO_nop6",
|
|
"RXO_nop7",
|
|
|
|
"RXO_scmpu",
|
|
"RXO_smovu",
|
|
"RXO_smovb",
|
|
"RXO_suntil",
|
|
"RXO_swhile",
|
|
"RXO_smovf",
|
|
"RXO_sstr",
|
|
|
|
"RXO_rmpa",
|
|
"RXO_mulhi",
|
|
"RXO_mullo",
|
|
"RXO_machi",
|
|
"RXO_maclo",
|
|
"RXO_mvtachi",
|
|
"RXO_mvtaclo",
|
|
"RXO_mvfachi",
|
|
"RXO_mvfacmi",
|
|
"RXO_mvfaclo",
|
|
"RXO_racw",
|
|
|
|
"RXO_sat", /* sat(d) */
|
|
"RXO_satr",
|
|
|
|
"RXO_fadd", /* d op= s */
|
|
"RXO_fcmp",
|
|
"RXO_fsub",
|
|
"RXO_ftoi",
|
|
"RXO_fmul",
|
|
"RXO_fdiv",
|
|
"RXO_round",
|
|
"RXO_itof",
|
|
|
|
"RXO_bset", /* d |= (1<<s) */
|
|
"RXO_bclr", /* d &= ~(1<<s) */
|
|
"RXO_btst", /* s & (1<<s2) */
|
|
"RXO_bnot", /* d ^= (1<<s) */
|
|
"RXO_bmcc", /* d<s> = cond(s2) */
|
|
|
|
"RXO_clrpsw", /* flag index in d */
|
|
"RXO_setpsw", /* flag index in d */
|
|
"RXO_mvtipl", /* new IPL in s */
|
|
|
|
"RXO_rtfi",
|
|
"RXO_rte",
|
|
"RXO_rtd", /* undocumented */
|
|
"RXO_brk",
|
|
"RXO_dbt", /* undocumented */
|
|
"RXO_int", /* vector id in s */
|
|
"RXO_stop",
|
|
"RXO_wait",
|
|
|
|
"RXO_sccnd", /* d = cond(s) ? 1 : 0 */
|
|
};
|
|
|
|
static const char * const optype_names[] = {
|
|
" - ",
|
|
"#Imm", /* #addend */
|
|
" Rn ", /* Rn */
|
|
"[Rn]", /* [Rn + addend] */
|
|
"Ps++", /* [Rn+] */
|
|
"--Pr", /* [-Rn] */
|
|
" cc ", /* eq, gtu, etc */
|
|
"Flag", /* [UIOSZC] */
|
|
"RbRi" /* [Rb + scale * Ri] */
|
|
};
|
|
|
|
#define N_RXO ARRAY_SIZE (id_names)
|
|
#define N_RXT ARRAY_SIZE (optype_names)
|
|
#define N_MAP 90
|
|
|
|
static unsigned long long benchmark_start_cycle;
|
|
static unsigned long long benchmark_end_cycle;
|
|
|
|
static int op_cache[N_RXT][N_RXT][N_RXT];
|
|
static int op_cache_rev[N_MAP];
|
|
static int op_cache_idx = 0;
|
|
|
|
static int
|
|
op_lookup (int a, int b, int c)
|
|
{
|
|
if (op_cache[a][b][c])
|
|
return op_cache[a][b][c];
|
|
op_cache_idx ++;
|
|
if (op_cache_idx >= N_MAP)
|
|
{
|
|
printf("op_cache_idx exceeds %d\n", N_MAP);
|
|
exit(1);
|
|
}
|
|
op_cache[a][b][c] = op_cache_idx;
|
|
op_cache_rev[op_cache_idx] = (a<<8) | (b<<4) | c;
|
|
return op_cache_idx;
|
|
}
|
|
|
|
static char *
|
|
op_cache_string (int map)
|
|
{
|
|
static int ci;
|
|
static char cb[5][20];
|
|
int a, b, c;
|
|
|
|
map = op_cache_rev[map];
|
|
a = (map >> 8) & 15;
|
|
b = (map >> 4) & 15;
|
|
c = (map >> 0) & 15;
|
|
ci = (ci + 1) % 5;
|
|
sprintf(cb[ci], "%s %s %s", optype_names[a], optype_names[b], optype_names[c]);
|
|
return cb[ci];
|
|
}
|
|
|
|
static unsigned long long cycles_per_id[N_RXO][N_MAP];
|
|
static unsigned long long times_per_id[N_RXO][N_MAP];
|
|
static unsigned long long memory_stalls;
|
|
static unsigned long long register_stalls;
|
|
static unsigned long long branch_stalls;
|
|
static unsigned long long branch_alignment_stalls;
|
|
static unsigned long long fast_returns;
|
|
|
|
static unsigned long times_per_pair[N_RXO][N_MAP][N_RXO][N_MAP];
|
|
static int prev_opcode_id = RXO_unknown;
|
|
static int po0;
|
|
|
|
#define STATS(x) x
|
|
|
|
#else
|
|
#define STATS(x)
|
|
#endif /* WITH_PROFILE */
|
|
|
|
|
|
#ifdef CYCLE_ACCURATE
|
|
|
|
static int new_rt = -1;
|
|
|
|
/* Number of cycles to add if an insn spans an 8-byte boundary. */
|
|
static int branch_alignment_penalty = 0;
|
|
|
|
#endif
|
|
|
|
static int running_benchmark = 1;
|
|
|
|
#define tprintf if (trace && running_benchmark) printf
|
|
|
|
jmp_buf decode_jmp_buf;
|
|
unsigned int rx_cycles = 0;
|
|
|
|
#ifdef CYCLE_ACCURATE
|
|
/* If nonzero, memory was read at some point and cycle latency might
|
|
take effect. */
|
|
static int memory_source = 0;
|
|
/* If nonzero, memory was written and extra cycles might be
|
|
needed. */
|
|
static int memory_dest = 0;
|
|
|
|
static void
|
|
cycles (int throughput)
|
|
{
|
|
tprintf("%d cycles\n", throughput);
|
|
regs.cycle_count += throughput;
|
|
}
|
|
|
|
/* Number of execution (E) cycles the op uses. For memory sources, we
|
|
include the load micro-op stall as two extra E cycles. */
|
|
#define E(c) cycles (memory_source ? c + 2 : c)
|
|
#define E1 cycles (1)
|
|
#define E2 cycles (2)
|
|
#define EBIT cycles (memory_source ? 2 : 1)
|
|
|
|
/* Check to see if a read latency must be applied for a given register. */
|
|
#define RL(r) \
|
|
if (regs.rt == r ) \
|
|
{ \
|
|
tprintf("register %d load stall\n", r); \
|
|
regs.cycle_count ++; \
|
|
STATS(register_stalls ++); \
|
|
regs.rt = -1; \
|
|
}
|
|
|
|
#define RLD(r) \
|
|
if (memory_source) \
|
|
{ \
|
|
tprintf ("Rt now %d\n", r); \
|
|
new_rt = r; \
|
|
}
|
|
|
|
static int
|
|
lsb_count (unsigned long v, int is_signed)
|
|
{
|
|
int i, lsb;
|
|
if (is_signed && (v & 0x80000000U))
|
|
v = (unsigned long)(long)(-v);
|
|
for (i=31; i>=0; i--)
|
|
if (v & (1 << i))
|
|
{
|
|
/* v is 0..31, we want 1=1-2, 2=3-4, 3=5-6, etc. */
|
|
lsb = (i + 2) / 2;
|
|
return lsb;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
divu_cycles(unsigned long num, unsigned long den)
|
|
{
|
|
int nb = lsb_count (num, 0);
|
|
int db = lsb_count (den, 0);
|
|
int rv;
|
|
|
|
if (nb < db)
|
|
rv = 2;
|
|
else
|
|
rv = 3 + nb - db;
|
|
E (rv);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
div_cycles(long num, long den)
|
|
{
|
|
int nb = lsb_count ((unsigned long)num, 1);
|
|
int db = lsb_count ((unsigned long)den, 1);
|
|
int rv;
|
|
|
|
if (nb < db)
|
|
rv = 3;
|
|
else
|
|
rv = 5 + nb - db;
|
|
E (rv);
|
|
return rv;
|
|
}
|
|
|
|
#else /* !CYCLE_ACCURATE */
|
|
|
|
#define cycles(t)
|
|
#define E(c)
|
|
#define E1
|
|
#define E2
|
|
#define EBIT
|
|
#define RL(r)
|
|
#define RLD(r)
|
|
|
|
#define divu_cycles(n,d)
|
|
#define div_cycles(n,d)
|
|
|
|
#endif /* else CYCLE_ACCURATE */
|
|
|
|
static const 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 ATTRIBUTE_NORETURN
|
|
_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 unsigned char *get_byte_base;
|
|
static RX_Opcode_Decoded **decode_cache_base;
|
|
static SI get_byte_page;
|
|
|
|
void
|
|
reset_decoder (void)
|
|
{
|
|
get_byte_base = 0;
|
|
decode_cache_base = 0;
|
|
get_byte_page = 0;
|
|
}
|
|
|
|
static inline void
|
|
maybe_get_mem_page (SI tpc)
|
|
{
|
|
if (((tpc ^ get_byte_page) & NONPAGE_MASK) || enable_counting)
|
|
{
|
|
get_byte_page = tpc & NONPAGE_MASK;
|
|
get_byte_base = rx_mem_ptr (get_byte_page, MPA_READING) - get_byte_page;
|
|
decode_cache_base = rx_mem_decode_cache (get_byte_page) - get_byte_page;
|
|
}
|
|
}
|
|
|
|
/* This gets called a *lot* so optimize it. */
|
|
static int
|
|
rx_get_byte (void *vdata)
|
|
{
|
|
RX_Data *rx_data = (RX_Data *)vdata;
|
|
SI tpc = rx_data->dpc;
|
|
|
|
/* See load.c for an explanation of this. */
|
|
if (rx_big_endian)
|
|
tpc ^= 3;
|
|
|
|
maybe_get_mem_page (tpc);
|
|
|
|
rx_data->dpc ++;
|
|
return get_byte_base [tpc];
|
|
}
|
|
|
|
static int
|
|
get_op (const RX_Opcode_Decoded *rd, int i)
|
|
{
|
|
const 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 */
|
|
RL (o->reg);
|
|
rv = get_reg (o->reg);
|
|
break;
|
|
|
|
case RX_Operand_Predec: /* [-Rn] */
|
|
put_reg (o->reg, get_reg (o->reg) - size2bytes[o->size]);
|
|
ATTRIBUTE_FALLTHROUGH;
|
|
case RX_Operand_Postinc: /* [Rn+] */
|
|
case RX_Operand_Zero_Indirect: /* [Rn + 0] */
|
|
case RX_Operand_Indirect: /* [Rn + addend] */
|
|
case RX_Operand_TwoReg: /* [Rn + scale * R2] */
|
|
#ifdef CYCLE_ACCURATE
|
|
RL (o->reg);
|
|
if (o->type == RX_Operand_TwoReg)
|
|
RL (rd->op[2].reg);
|
|
regs.rt = -1;
|
|
if (regs.m2m == M2M_BOTH)
|
|
{
|
|
tprintf("src memory stall\n");
|
|
#ifdef WITH_PROFILE
|
|
memory_stalls ++;
|
|
#endif
|
|
regs.cycle_count ++;
|
|
regs.m2m = 0;
|
|
}
|
|
|
|
memory_source = 1;
|
|
#endif
|
|
|
|
if (o->type == RX_Operand_TwoReg)
|
|
addr = get_reg (o->reg) * size2bytes[rd->size] + get_reg (rd->op[2].reg);
|
|
else
|
|
addr = get_reg (o->reg) + o->addend;
|
|
|
|
switch (o->size)
|
|
{
|
|
default:
|
|
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)
|
|
{
|
|
default:
|
|
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 (const RX_Opcode_Decoded *rd, int i, int v)
|
|
{
|
|
const RX_Opcode_Operand *o = rd->op + i;
|
|
int addr;
|
|
|
|
switch (o->size)
|
|
{
|
|
default:
|
|
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);
|
|
RLD (o->reg);
|
|
break;
|
|
|
|
case RX_Operand_Predec: /* [-Rn] */
|
|
put_reg (o->reg, get_reg (o->reg) - size2bytes[o->size]);
|
|
ATTRIBUTE_FALLTHROUGH;
|
|
case RX_Operand_Postinc: /* [Rn+] */
|
|
case RX_Operand_Zero_Indirect: /* [Rn + 0] */
|
|
case RX_Operand_Indirect: /* [Rn + addend] */
|
|
case RX_Operand_TwoReg: /* [Rn + scale * R2] */
|
|
|
|
#ifdef CYCLE_ACCURATE
|
|
if (regs.m2m == M2M_BOTH)
|
|
{
|
|
tprintf("dst memory stall\n");
|
|
regs.cycle_count ++;
|
|
#ifdef WITH_PROFILE
|
|
memory_stalls ++;
|
|
#endif
|
|
regs.m2m = 0;
|
|
}
|
|
memory_dest = 1;
|
|
#endif
|
|
|
|
if (o->type == RX_Operand_TwoReg)
|
|
addr = get_reg (o->reg) * size2bytes[rd->size] + get_reg (rd->op[2].reg);
|
|
else
|
|
addr = get_reg (o->reg) + o->addend;
|
|
|
|
switch (o->size)
|
|
{
|
|
default:
|
|
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 (void)
|
|
{
|
|
int rv;
|
|
int rsp = get_reg (sp);
|
|
rv = mem_get_si (rsp);
|
|
rsp += 4;
|
|
put_reg (sp, rsp);
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
poppc (void)
|
|
{
|
|
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) \
|
|
{ \
|
|
umb = US2(); \
|
|
uma = US1(); \
|
|
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); \
|
|
E (1); \
|
|
}
|
|
|
|
#define LOGIC_OP(vop) \
|
|
{ \
|
|
mb = US2(); \
|
|
ma = US1(); \
|
|
v = ma vop mb; \
|
|
tprintf("0x%x " #vop " 0x%x = 0x%x\n", ma, mb, v); \
|
|
set_sz (v, DSZ()); \
|
|
PD(v); \
|
|
E (1); \
|
|
}
|
|
|
|
#define SHIFT_OP(val, type, count, OP, carry_mask) \
|
|
{ \
|
|
int i, c=0; \
|
|
count = US2(); \
|
|
val = (type)US1(); \
|
|
tprintf("%lld " #OP " %d\n", val, count); \
|
|
for (i = 0; i < count; i ++) \
|
|
{ \
|
|
c = val & carry_mask; \
|
|
val OP 1; \
|
|
} \
|
|
set_oszc (val, 4, c); \
|
|
PD (val); \
|
|
}
|
|
|
|
typedef union {
|
|
int i;
|
|
float f;
|
|
} FloatInt;
|
|
|
|
ATTRIBUTE_UNUSED
|
|
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(); \
|
|
fb = GS (); \
|
|
fa = GD (); \
|
|
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 ();
|
|
}
|
|
|
|
static int
|
|
op_is_memory (const RX_Opcode_Decoded *rd, int i)
|
|
{
|
|
switch (rd->op[i].type)
|
|
{
|
|
case RX_Operand_Predec:
|
|
case RX_Operand_Postinc:
|
|
case RX_Operand_Indirect:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
#define OM(i) op_is_memory (opcode, i)
|
|
|
|
#define DO_RETURN(x) { longjmp (decode_jmp_buf, x); }
|
|
|
|
int
|
|
decode_opcode (void)
|
|
{
|
|
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;
|
|
const RX_Opcode_Decoded *opcode;
|
|
#ifdef WITH_PROFILE
|
|
unsigned long long prev_cycle_count;
|
|
#endif
|
|
#ifdef CYCLE_ACCURATE
|
|
unsigned int tx;
|
|
#endif
|
|
|
|
#ifdef WITH_PROFILE
|
|
prev_cycle_count = regs.cycle_count;
|
|
#endif
|
|
|
|
#ifdef CYCLE_ACCURATE
|
|
memory_source = 0;
|
|
memory_dest = 0;
|
|
#endif
|
|
|
|
rx_cycles ++;
|
|
|
|
maybe_get_mem_page (regs.r_pc);
|
|
|
|
opcode_pc = regs.r_pc;
|
|
|
|
/* Note that we don't word-swap this point, there's no point. */
|
|
if (decode_cache_base[opcode_pc] == NULL)
|
|
{
|
|
RX_Opcode_Decoded *opcode_w;
|
|
rx_data.dpc = opcode_pc;
|
|
opcode_w = decode_cache_base[opcode_pc] = calloc (1, sizeof (RX_Opcode_Decoded));
|
|
opcode_size = rx_decode_opcode (opcode_pc, opcode_w,
|
|
rx_get_byte, &rx_data);
|
|
opcode = opcode_w;
|
|
}
|
|
else
|
|
{
|
|
opcode = decode_cache_base[opcode_pc];
|
|
opcode_size = opcode->n_bytes;
|
|
}
|
|
|
|
#ifdef CYCLE_ACCURATE
|
|
if (branch_alignment_penalty)
|
|
{
|
|
if ((regs.r_pc ^ (regs.r_pc + opcode_size - 1)) & ~7)
|
|
{
|
|
tprintf("1 cycle branch alignment penalty\n");
|
|
cycles (branch_alignment_penalty);
|
|
#ifdef WITH_PROFILE
|
|
branch_alignment_stalls ++;
|
|
#endif
|
|
}
|
|
branch_alignment_penalty = 0;
|
|
}
|
|
#endif
|
|
|
|
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);
|
|
E (1);
|
|
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);
|
|
EBIT;
|
|
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);
|
|
EBIT;
|
|
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);
|
|
EBIT;
|
|
break;
|
|
|
|
case RXO_branch:
|
|
if (opcode->op[1].type == RX_Operand_None || GS())
|
|
{
|
|
#ifdef CYCLE_ACCURATE
|
|
SI old_pc = regs.r_pc;
|
|
int delta;
|
|
#endif
|
|
regs.r_pc = GD();
|
|
#ifdef CYCLE_ACCURATE
|
|
delta = regs.r_pc - old_pc;
|
|
if (delta >= 0 && delta < 16
|
|
&& opcode_size > 1)
|
|
{
|
|
tprintf("near forward branch bonus\n");
|
|
cycles (2);
|
|
}
|
|
else
|
|
{
|
|
cycles (3);
|
|
branch_alignment_penalty = 1;
|
|
}
|
|
#ifdef WITH_PROFILE
|
|
branch_stalls ++;
|
|
#endif
|
|
#endif
|
|
}
|
|
#ifdef CYCLE_ACCURATE
|
|
else
|
|
cycles (1);
|
|
#endif
|
|
break;
|
|
|
|
case RXO_branchrel:
|
|
if (opcode->op[1].type == RX_Operand_None || GS())
|
|
{
|
|
int delta = GD();
|
|
regs.r_pc = opcode_pc + delta;
|
|
#ifdef CYCLE_ACCURATE
|
|
/* Note: specs say 3, chip says 2. */
|
|
if (delta >= 0 && delta < 16
|
|
&& opcode_size > 1)
|
|
{
|
|
tprintf("near forward branch bonus\n");
|
|
cycles (2);
|
|
}
|
|
else
|
|
{
|
|
cycles (3);
|
|
branch_alignment_penalty = 1;
|
|
}
|
|
#ifdef WITH_PROFILE
|
|
branch_stalls ++;
|
|
#endif
|
|
#endif
|
|
}
|
|
#ifdef CYCLE_ACCURATE
|
|
else
|
|
cycles (1);
|
|
#endif
|
|
break;
|
|
|
|
case RXO_brk:
|
|
{
|
|
int old_psw = regs.r_psw;
|
|
if (rx_in_gdb)
|
|
DO_RETURN (RX_MAKE_HIT_BREAK ());
|
|
if (regs.r_intb == 0)
|
|
{
|
|
tprintf("BREAK hit, no vector table.\n");
|
|
DO_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);
|
|
cycles(6);
|
|
}
|
|
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);
|
|
EBIT;
|
|
break;
|
|
|
|
case RXO_btst:
|
|
ma = GS ();
|
|
mb = GS2 ();
|
|
if (opcode->op[1].type == RX_Operand_Register)
|
|
mb &= 0x1f;
|
|
else
|
|
mb &= 0x07;
|
|
umb = ma & (1 << mb);
|
|
set_zc (! umb, umb);
|
|
EBIT;
|
|
break;
|
|
|
|
case RXO_clrpsw:
|
|
v = 1 << opcode->op[0].reg;
|
|
if (FLAG_PM
|
|
&& (v == FLAGBIT_I
|
|
|| v == FLAGBIT_U))
|
|
break;
|
|
regs.r_psw &= ~v;
|
|
cycles (1);
|
|
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);
|
|
cycles (3);
|
|
}
|
|
else
|
|
{
|
|
v = mb/ma;
|
|
tprintf("%d\n", v);
|
|
set_flags (FLAGBIT_O, 0);
|
|
PD (v);
|
|
div_cycles (mb, ma);
|
|
}
|
|
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);
|
|
cycles (2);
|
|
}
|
|
else
|
|
{
|
|
v = umb / uma;
|
|
tprintf("%u\n", v);
|
|
set_flags (FLAGBIT_O, 0);
|
|
PD (v);
|
|
divu_cycles (umb, uma);
|
|
}
|
|
break;
|
|
|
|
case RXO_emul:
|
|
ma = GD ();
|
|
mb = GS ();
|
|
sll = (long long)ma * (long long)mb;
|
|
tprintf("%d * %d = %lld\n", ma, mb, sll);
|
|
put_reg (opcode->op[0].reg, sll);
|
|
put_reg (opcode->op[0].reg + 1, sll >> 32);
|
|
E2;
|
|
break;
|
|
|
|
case RXO_emulu:
|
|
uma = GD ();
|
|
umb = GS ();
|
|
ll = (long long)uma * (long long)umb;
|
|
tprintf("%#x * %#x = %#llx\n", uma, umb, ll);
|
|
put_reg (opcode->op[0].reg, ll);
|
|
put_reg (opcode->op[0].reg + 1, ll >> 32);
|
|
E2;
|
|
break;
|
|
|
|
case RXO_fadd:
|
|
FLOAT_OP (fadd);
|
|
E (4);
|
|
break;
|
|
|
|
case RXO_fcmp:
|
|
ma = GD();
|
|
mb = GS();
|
|
FPCLEAR ();
|
|
rxfp_cmp (ma, mb);
|
|
FPCHECK ();
|
|
E (1);
|
|
break;
|
|
|
|
case RXO_fdiv:
|
|
FLOAT_OP (fdiv);
|
|
E (16);
|
|
break;
|
|
|
|
case RXO_fmul:
|
|
FLOAT_OP (fmul);
|
|
E (3);
|
|
break;
|
|
|
|
case RXO_rtfi:
|
|
PRIVILEDGED ();
|
|
regs.r_psw = regs.r_bpsw;
|
|
regs.r_pc = regs.r_bpc;
|
|
#ifdef CYCLE_ACCURATE
|
|
regs.fast_return = 0;
|
|
cycles(3);
|
|
#endif
|
|
break;
|
|
|
|
case RXO_fsub:
|
|
FLOAT_OP (fsub);
|
|
E (4);
|
|
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);
|
|
E (2);
|
|
break;
|
|
|
|
case RXO_int:
|
|
v = GS ();
|
|
if (v == 255)
|
|
{
|
|
int rc = rx_syscall (regs.r[5]);
|
|
if (! RX_STEPPED (rc))
|
|
DO_RETURN (rc);
|
|
}
|
|
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);
|
|
}
|
|
cycles (6);
|
|
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);
|
|
E (2);
|
|
break;
|
|
|
|
case RXO_jsr:
|
|
case RXO_jsrrel:
|
|
{
|
|
#ifdef CYCLE_ACCURATE
|
|
int delta;
|
|
regs.m2m = 0;
|
|
#endif
|
|
v = GD ();
|
|
#ifdef CYCLE_ACCURATE
|
|
regs.link_register = regs.r_pc;
|
|
#endif
|
|
pushpc (get_reg (pc));
|
|
if (opcode->id == RXO_jsrrel)
|
|
v += regs.r_pc;
|
|
#ifdef CYCLE_ACCURATE
|
|
delta = v - regs.r_pc;
|
|
#endif
|
|
put_reg (pc, v);
|
|
#ifdef CYCLE_ACCURATE
|
|
/* Note: docs say 3, chip says 2 */
|
|
if (delta >= 0 && delta < 16)
|
|
{
|
|
tprintf ("near forward jsr bonus\n");
|
|
cycles (2);
|
|
}
|
|
else
|
|
{
|
|
branch_alignment_penalty = 1;
|
|
cycles (3);
|
|
}
|
|
regs.fast_return = 1;
|
|
#endif
|
|
}
|
|
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);
|
|
E1;
|
|
break;
|
|
|
|
case RXO_maclo:
|
|
ll = (long long)(signed short)(GS()) * (long long)(signed short)(GS2 ());
|
|
ll <<= 16;
|
|
put_reg64 (acc64, ll + regs.r_acc);
|
|
E1;
|
|
break;
|
|
|
|
case RXO_max:
|
|
mb = GS();
|
|
ma = GD();
|
|
if (ma > mb)
|
|
PD (ma);
|
|
else
|
|
PD (mb);
|
|
E (1);
|
|
break;
|
|
|
|
case RXO_min:
|
|
mb = GS();
|
|
ma = GD();
|
|
if (ma < mb)
|
|
PD (ma);
|
|
else
|
|
PD (mb);
|
|
E (1);
|
|
break;
|
|
|
|
case RXO_mov:
|
|
v = GS ();
|
|
|
|
if (opcode->op[1].type == RX_Operand_Register
|
|
&& opcode->op[1].reg == 17 /* PC */)
|
|
{
|
|
/* Special case. We want the address of the insn, not the
|
|
address of the next insn. */
|
|
v = opcode_pc;
|
|
}
|
|
|
|
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;
|
|
}
|
|
if (OM(0) && OM(1))
|
|
cycles (2);
|
|
else
|
|
cycles (1);
|
|
|
|
PD (v);
|
|
|
|
#ifdef CYCLE_ACCURATE
|
|
if ((opcode->op[0].type == RX_Operand_Predec
|
|
&& opcode->op[1].type == RX_Operand_Register)
|
|
|| (opcode->op[0].type == RX_Operand_Postinc
|
|
&& opcode->op[1].type == RX_Operand_Register))
|
|
{
|
|
/* Special case: push reg doesn't cause a memory stall. */
|
|
memory_dest = 0;
|
|
tprintf("push special case\n");
|
|
}
|
|
#endif
|
|
|
|
set_sz (v, DSZ());
|
|
break;
|
|
|
|
case RXO_movbi:
|
|
PD (GS ());
|
|
cycles (1);
|
|
break;
|
|
|
|
case RXO_movbir:
|
|
PS (GD ());
|
|
cycles (1);
|
|
break;
|
|
|
|
case RXO_mul:
|
|
v = US2 ();
|
|
ll = (unsigned long long) US1() * (unsigned long long) v;
|
|
PD(ll);
|
|
E (1);
|
|
break;
|
|
|
|
case RXO_mulhi:
|
|
v = GS2 ();
|
|
ll = (long long)(signed short)(GS() >> 16) * (long long)(signed short)(v >> 16);
|
|
ll <<= 16;
|
|
put_reg64 (acc64, ll);
|
|
E1;
|
|
break;
|
|
|
|
case RXO_mullo:
|
|
v = GS2 ();
|
|
ll = (long long)(signed short)(GS()) * (long long)(signed short)(v);
|
|
ll <<= 16;
|
|
put_reg64 (acc64, ll);
|
|
E1;
|
|
break;
|
|
|
|
case RXO_mvfachi:
|
|
PD (get_reg (acchi));
|
|
E1;
|
|
break;
|
|
|
|
case RXO_mvfaclo:
|
|
PD (get_reg (acclo));
|
|
E1;
|
|
break;
|
|
|
|
case RXO_mvfacmi:
|
|
PD (get_reg (accmi));
|
|
E1;
|
|
break;
|
|
|
|
case RXO_mvtachi:
|
|
put_reg (acchi, GS ());
|
|
E1;
|
|
break;
|
|
|
|
case RXO_mvtaclo:
|
|
put_reg (acclo, GS ());
|
|
E1;
|
|
break;
|
|
|
|
case RXO_mvtipl:
|
|
regs.r_psw &= ~ FLAGBITS_IPL;
|
|
regs.r_psw |= (GS () << FLAGSHIFT_IPL) & FLAGBITS_IPL;
|
|
E1;
|
|
break;
|
|
|
|
case RXO_nop:
|
|
case RXO_nop2:
|
|
case RXO_nop3:
|
|
case RXO_nop4:
|
|
case RXO_nop5:
|
|
case RXO_nop6:
|
|
case RXO_nop7:
|
|
E1;
|
|
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;
|
|
DO_RETURN (RX_MAKE_STOPPED (SIGILL));
|
|
}
|
|
for (v = opcode->op[1].reg; v <= opcode->op[2].reg; v++)
|
|
{
|
|
cycles (1);
|
|
RLD (v);
|
|
put_reg (v, pop ());
|
|
}
|
|
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--)
|
|
{
|
|
RL (v);
|
|
push (get_reg (v));
|
|
}
|
|
cycles (opcode->op[2].reg - opcode->op[1].reg + 1);
|
|
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);
|
|
E1;
|
|
break;
|
|
|
|
case RXO_rte:
|
|
PRIVILEDGED ();
|
|
regs.r_pc = poppc ();
|
|
regs.r_psw = poppc ();
|
|
if (FLAG_PM)
|
|
regs.r_psw |= FLAGBIT_U;
|
|
#ifdef CYCLE_ACCURATE
|
|
regs.fast_return = 0;
|
|
cycles (6);
|
|
#endif
|
|
break;
|
|
|
|
case RXO_revl:
|
|
uma = GS ();
|
|
umb = (((uma >> 24) & 0xff)
|
|
| ((uma >> 8) & 0xff00)
|
|
| ((uma << 8) & 0xff0000)
|
|
| ((uma << 24) & 0xff000000UL));
|
|
PD (umb);
|
|
E1;
|
|
break;
|
|
|
|
case RXO_revw:
|
|
uma = GS ();
|
|
umb = (((uma >> 8) & 0x00ff00ff)
|
|
| ((uma << 8) & 0xff00ff00UL));
|
|
PD (umb);
|
|
E1;
|
|
break;
|
|
|
|
case RXO_rmpa:
|
|
RL(4);
|
|
RL(5);
|
|
#ifdef CYCLE_ACCURATE
|
|
tx = regs.r[3];
|
|
#endif
|
|
|
|
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);
|
|
#ifdef CYCLE_ACCURATE
|
|
switch (opcode->size)
|
|
{
|
|
case RX_Long:
|
|
cycles (6 + 4 * tx);
|
|
break;
|
|
case RX_Word:
|
|
cycles (6 + 5 * (tx / 2) + 4 * (tx % 2));
|
|
break;
|
|
case RX_Byte:
|
|
cycles (6 + 7 * (tx / 4) + 4 * (tx % 4));
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case RXO_rolc:
|
|
v = GD ();
|
|
ma = v & 0x80000000UL;
|
|
v <<= 1;
|
|
v |= carry;
|
|
set_szc (v, 4, ma);
|
|
PD (v);
|
|
E1;
|
|
break;
|
|
|
|
case RXO_rorc:
|
|
uma = GD ();
|
|
mb = uma & 1;
|
|
uma >>= 1;
|
|
uma |= (carry ? 0x80000000UL : 0);
|
|
set_szc (uma, 4, mb);
|
|
PD (uma);
|
|
E1;
|
|
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);
|
|
E1;
|
|
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);
|
|
E1;
|
|
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);
|
|
E (2);
|
|
break;
|
|
|
|
case RXO_rts:
|
|
{
|
|
#ifdef CYCLE_ACCURATE
|
|
int cyc = 5;
|
|
#endif
|
|
regs.r_pc = poppc ();
|
|
#ifdef CYCLE_ACCURATE
|
|
/* Note: specs say 5, chip says 3. */
|
|
if (regs.fast_return && regs.link_register == regs.r_pc)
|
|
{
|
|
#ifdef WITH_PROFILE
|
|
fast_returns ++;
|
|
#endif
|
|
tprintf("fast return bonus\n");
|
|
cyc -= 2;
|
|
}
|
|
cycles (cyc);
|
|
regs.fast_return = 0;
|
|
branch_alignment_penalty = 1;
|
|
#endif
|
|
}
|
|
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);
|
|
#ifdef CYCLE_ACCURATE
|
|
tx = opcode->op[0].reg - opcode->op[2].reg + 1;
|
|
#endif
|
|
for (i = opcode->op[2].reg; i <= opcode->op[0].reg; i ++)
|
|
{
|
|
RLD (i);
|
|
put_reg (i, pop ());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef CYCLE_ACCURATE
|
|
tx = 0;
|
|
#endif
|
|
put_reg (0, get_reg (0) + GS());
|
|
}
|
|
put_reg (pc, poppc());
|
|
#ifdef CYCLE_ACCURATE
|
|
if (regs.fast_return && regs.link_register == regs.r_pc)
|
|
{
|
|
tprintf("fast return bonus\n");
|
|
#ifdef WITH_PROFILE
|
|
fast_returns ++;
|
|
#endif
|
|
cycles (tx < 3 ? 3 : tx + 1);
|
|
}
|
|
else
|
|
{
|
|
cycles (tx < 5 ? 5 : tx + 1);
|
|
}
|
|
regs.fast_return = 0;
|
|
branch_alignment_penalty = 1;
|
|
#endif
|
|
break;
|
|
|
|
case RXO_sat:
|
|
if (FLAG_O && FLAG_S)
|
|
PD (0x7fffffffUL);
|
|
else if (FLAG_O && ! FLAG_S)
|
|
PD (0x80000000UL);
|
|
E1;
|
|
break;
|
|
|
|
case RXO_satr:
|
|
if (FLAG_O && ! FLAG_S)
|
|
{
|
|
put_reg (6, 0x0);
|
|
put_reg (5, 0x7fffffff);
|
|
put_reg (4, 0xffffffff);
|
|
}
|
|
else if (FLAG_O && FLAG_S)
|
|
{
|
|
put_reg (6, 0xffffffff);
|
|
put_reg (5, 0x80000000);
|
|
put_reg (4, 0x0);
|
|
}
|
|
E1;
|
|
break;
|
|
|
|
case RXO_sbb:
|
|
MATH_OP (-, ! carry);
|
|
break;
|
|
|
|
case RXO_sccnd:
|
|
if (GS())
|
|
PD (1);
|
|
else
|
|
PD (0);
|
|
E1;
|
|
break;
|
|
|
|
case RXO_scmpu:
|
|
#ifdef CYCLE_ACCURATE
|
|
tx = regs.r[3];
|
|
#endif
|
|
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);
|
|
cycles (2 + 4 * (tx / 4) + 4 * (tx % 4));
|
|
break;
|
|
|
|
case RXO_setpsw:
|
|
v = 1 << opcode->op[0].reg;
|
|
if (FLAG_PM
|
|
&& (v == FLAGBIT_I
|
|
|| v == FLAGBIT_U))
|
|
break;
|
|
regs.r_psw |= v;
|
|
cycles (1);
|
|
break;
|
|
|
|
case RXO_smovb:
|
|
RL (3);
|
|
#ifdef CYCLE_ACCURATE
|
|
tx = regs.r[3];
|
|
#endif
|
|
while (regs.r[3])
|
|
{
|
|
uma = mem_get_qi (regs.r[2] --);
|
|
mem_put_qi (regs.r[1]--, uma);
|
|
regs.r[3] --;
|
|
}
|
|
#ifdef CYCLE_ACCURATE
|
|
if (tx > 3)
|
|
cycles (6 + 3 * (tx / 4) + 3 * (tx % 4));
|
|
else
|
|
cycles (2 + 3 * (tx % 4));
|
|
#endif
|
|
break;
|
|
|
|
case RXO_smovf:
|
|
RL (3);
|
|
#ifdef CYCLE_ACCURATE
|
|
tx = regs.r[3];
|
|
#endif
|
|
while (regs.r[3])
|
|
{
|
|
uma = mem_get_qi (regs.r[2] ++);
|
|
mem_put_qi (regs.r[1]++, uma);
|
|
regs.r[3] --;
|
|
}
|
|
cycles (2 + 3 * (int)(tx / 4) + 3 * (tx % 4));
|
|
break;
|
|
|
|
case RXO_smovu:
|
|
#ifdef CYCLE_ACCURATE
|
|
tx = regs.r[3];
|
|
#endif
|
|
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;
|
|
}
|
|
cycles (2 + 3 * (int)(tx / 4) + 3 * (tx % 4));
|
|
break;
|
|
|
|
case RXO_shar: /* d = ma >> mb */
|
|
SHIFT_OP (sll, int, mb, >>=, 1);
|
|
E (1);
|
|
break;
|
|
|
|
case RXO_shll: /* d = ma << mb */
|
|
SHIFT_OP (ll, int, mb, <<=, 0x80000000UL);
|
|
E (1);
|
|
break;
|
|
|
|
case RXO_shlr: /* d = ma >> mb */
|
|
SHIFT_OP (ll, unsigned int, mb, >>=, 1);
|
|
E (1);
|
|
break;
|
|
|
|
case RXO_sstr:
|
|
RL (3);
|
|
#ifdef CYCLE_ACCURATE
|
|
tx = regs.r[3];
|
|
#endif
|
|
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] --;
|
|
}
|
|
cycles (2 + tx);
|
|
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] --;
|
|
}
|
|
cycles (2 + (int)(tx / 2) + tx % 2);
|
|
break;
|
|
case RX_Byte:
|
|
while (regs.r[3] != 0)
|
|
{
|
|
mem_put_qi (regs.r[1], regs.r[2]);
|
|
regs.r[1] ++;
|
|
regs.r[3] --;
|
|
}
|
|
cycles (2 + (int)(tx / 4) + tx % 4);
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
break;
|
|
|
|
case RXO_stcc:
|
|
if (GS2())
|
|
PD (GS ());
|
|
E1;
|
|
break;
|
|
|
|
case RXO_stop:
|
|
PRIVILEDGED ();
|
|
regs.r_psw |= FLAGBIT_I;
|
|
DO_RETURN (RX_MAKE_STOPPED(0));
|
|
|
|
case RXO_sub:
|
|
MATH_OP (-, 0);
|
|
break;
|
|
|
|
case RXO_suntil:
|
|
RL(3);
|
|
#ifdef CYCLE_ACCURATE
|
|
tx = 0;
|
|
#endif
|
|
if (regs.r[3] == 0)
|
|
{
|
|
cycles (3);
|
|
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;
|
|
#ifdef CYCLE_ACCURATE
|
|
tx ++;
|
|
#endif
|
|
if (umb == uma)
|
|
break;
|
|
}
|
|
#ifdef CYCLE_ACCURATE
|
|
cycles (3 + 3 * tx);
|
|
#endif
|
|
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;
|
|
#ifdef CYCLE_ACCURATE
|
|
tx ++;
|
|
#endif
|
|
if (umb == uma)
|
|
break;
|
|
}
|
|
#ifdef CYCLE_ACCURATE
|
|
cycles (3 + 3 * (tx / 2) + 3 * (tx % 2));
|
|
#endif
|
|
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;
|
|
#ifdef CYCLE_ACCURATE
|
|
tx ++;
|
|
#endif
|
|
if (umb == uma)
|
|
break;
|
|
}
|
|
#ifdef CYCLE_ACCURATE
|
|
cycles (3 + 3 * (tx / 4) + 3 * (tx % 4));
|
|
#endif
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
if (uma == umb)
|
|
set_zc (1, 1);
|
|
else
|
|
set_zc (0, ((int)uma - (int)umb) >= 0);
|
|
break;
|
|
|
|
case RXO_swhile:
|
|
RL(3);
|
|
#ifdef CYCLE_ACCURATE
|
|
tx = 0;
|
|
#endif
|
|
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;
|
|
#ifdef CYCLE_ACCURATE
|
|
tx ++;
|
|
#endif
|
|
if (umb != uma)
|
|
break;
|
|
}
|
|
#ifdef CYCLE_ACCURATE
|
|
cycles (3 + 3 * tx);
|
|
#endif
|
|
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;
|
|
#ifdef CYCLE_ACCURATE
|
|
tx ++;
|
|
#endif
|
|
if (umb != uma)
|
|
break;
|
|
}
|
|
#ifdef CYCLE_ACCURATE
|
|
cycles (3 + 3 * (tx / 2) + 3 * (tx % 2));
|
|
#endif
|
|
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;
|
|
#ifdef CYCLE_ACCURATE
|
|
tx ++;
|
|
#endif
|
|
if (umb != uma)
|
|
break;
|
|
}
|
|
#ifdef CYCLE_ACCURATE
|
|
cycles (3 + 3 * (tx / 4) + 3 * (tx % 4));
|
|
#endif
|
|
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;
|
|
DO_RETURN (RX_MAKE_STOPPED(0));
|
|
|
|
case RXO_xchg:
|
|
#ifdef CYCLE_ACCURATE
|
|
regs.m2m = 0;
|
|
#endif
|
|
v = GS (); /* This is the memory operand, if any. */
|
|
PS (GD ()); /* and this may change the address register. */
|
|
PD (v);
|
|
E2;
|
|
#ifdef CYCLE_ACCURATE
|
|
/* all M cycles happen during xchg's cycles. */
|
|
memory_dest = 0;
|
|
memory_source = 0;
|
|
#endif
|
|
break;
|
|
|
|
case RXO_xor:
|
|
LOGIC_OP (^);
|
|
break;
|
|
|
|
default:
|
|
EXCEPTION (EX_UNDEFINED);
|
|
}
|
|
|
|
#ifdef CYCLE_ACCURATE
|
|
regs.m2m = 0;
|
|
if (memory_source)
|
|
regs.m2m |= M2M_SRC;
|
|
if (memory_dest)
|
|
regs.m2m |= M2M_DST;
|
|
|
|
regs.rt = new_rt;
|
|
new_rt = -1;
|
|
#endif
|
|
|
|
#ifdef WITH_PROFILE
|
|
if (prev_cycle_count == regs.cycle_count)
|
|
{
|
|
printf("Cycle count not updated! id %s\n", id_names[opcode->id]);
|
|
abort ();
|
|
}
|
|
#endif
|
|
|
|
#ifdef WITH_PROFILE
|
|
if (running_benchmark)
|
|
{
|
|
int omap = op_lookup (opcode->op[0].type, opcode->op[1].type, opcode->op[2].type);
|
|
|
|
|
|
cycles_per_id[opcode->id][omap] += regs.cycle_count - prev_cycle_count;
|
|
times_per_id[opcode->id][omap] ++;
|
|
|
|
times_per_pair[prev_opcode_id][po0][opcode->id][omap] ++;
|
|
|
|
prev_opcode_id = opcode->id;
|
|
po0 = omap;
|
|
}
|
|
#endif
|
|
|
|
return RX_MAKE_STEPPED ();
|
|
}
|
|
|
|
#ifdef WITH_PROFILE
|
|
void
|
|
reset_pipeline_stats (void)
|
|
{
|
|
memset (cycles_per_id, 0, sizeof(cycles_per_id));
|
|
memset (times_per_id, 0, sizeof(times_per_id));
|
|
memory_stalls = 0;
|
|
register_stalls = 0;
|
|
branch_stalls = 0;
|
|
branch_alignment_stalls = 0;
|
|
fast_returns = 0;
|
|
memset (times_per_pair, 0, sizeof(times_per_pair));
|
|
running_benchmark = 1;
|
|
|
|
benchmark_start_cycle = regs.cycle_count;
|
|
}
|
|
|
|
void
|
|
halt_pipeline_stats (void)
|
|
{
|
|
running_benchmark = 0;
|
|
benchmark_end_cycle = regs.cycle_count;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
pipeline_stats (void)
|
|
{
|
|
#ifdef WITH_PROFILE
|
|
int i, o1;
|
|
int p, p1;
|
|
#endif
|
|
|
|
#ifdef CYCLE_ACCURATE
|
|
if (verbose == 1)
|
|
{
|
|
printf ("cycles: %llu\n", regs.cycle_count);
|
|
return;
|
|
}
|
|
|
|
printf ("cycles: %13s\n", comma (regs.cycle_count));
|
|
#endif
|
|
|
|
#ifdef WITH_PROFILE
|
|
if (benchmark_start_cycle)
|
|
printf ("bmark: %13s\n", comma (benchmark_end_cycle - benchmark_start_cycle));
|
|
|
|
printf("\n");
|
|
for (i = 0; i < N_RXO; i++)
|
|
for (o1 = 0; o1 < N_MAP; o1 ++)
|
|
if (times_per_id[i][o1])
|
|
printf("%13s %13s %7.2f %s %s\n",
|
|
comma (cycles_per_id[i][o1]),
|
|
comma (times_per_id[i][o1]),
|
|
(double)cycles_per_id[i][o1] / times_per_id[i][o1],
|
|
op_cache_string(o1),
|
|
id_names[i]+4);
|
|
|
|
printf("\n");
|
|
for (p = 0; p < N_RXO; p ++)
|
|
for (p1 = 0; p1 < N_MAP; p1 ++)
|
|
for (i = 0; i < N_RXO; i ++)
|
|
for (o1 = 0; o1 < N_MAP; o1 ++)
|
|
if (times_per_pair[p][p1][i][o1])
|
|
{
|
|
printf("%13s %s %-9s -> %s %s\n",
|
|
comma (times_per_pair[p][p1][i][o1]),
|
|
op_cache_string(p1),
|
|
id_names[p]+4,
|
|
op_cache_string(o1),
|
|
id_names[i]+4);
|
|
}
|
|
|
|
printf("\n");
|
|
printf("%13s memory stalls\n", comma (memory_stalls));
|
|
printf("%13s register stalls\n", comma (register_stalls));
|
|
printf("%13s branches taken (non-return)\n", comma (branch_stalls));
|
|
printf("%13s branch alignment stalls\n", comma (branch_alignment_stalls));
|
|
printf("%13s fast returns\n", comma (fast_returns));
|
|
#endif
|
|
}
|