mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:51:15 +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.
1601 lines
43 KiB
C
1601 lines
43 KiB
C
/* frv simulator support code
|
||
Copyright (C) 1998-2024 Free Software Foundation, Inc.
|
||
Contributed by Red Hat.
|
||
|
||
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"
|
||
|
||
#define WANT_CPU
|
||
#define WANT_CPU_FRVBF
|
||
|
||
#include "sim-main.h"
|
||
#include "cgen-mem.h"
|
||
#include "cgen-ops.h"
|
||
#include "cgen-engine.h"
|
||
#include "cgen-par.h"
|
||
#include "bfd.h"
|
||
#include "sim/sim-frv.h"
|
||
#include <math.h>
|
||
#include <stdlib.h>
|
||
|
||
/* Maintain a flag in order to know when to write the address of the next
|
||
VLIW instruction into the LR register. Used by JMPL. JMPIL, and CALL
|
||
insns. */
|
||
int frvbf_write_next_vliw_addr_to_LR;
|
||
|
||
/* The contents of BUF are in target byte order. */
|
||
int
|
||
frvbf_fetch_register (SIM_CPU *current_cpu, int rn, void *buf, int len)
|
||
{
|
||
if (SIM_FRV_GR0_REGNUM <= rn && rn <= SIM_FRV_GR63_REGNUM)
|
||
{
|
||
int hi_available, lo_available;
|
||
int grn = rn - SIM_FRV_GR0_REGNUM;
|
||
|
||
frv_gr_registers_available (current_cpu, &hi_available, &lo_available);
|
||
|
||
if ((grn < 32 && !lo_available) || (grn >= 32 && !hi_available))
|
||
return 0;
|
||
else
|
||
SETTSI (buf, GET_H_GR (grn));
|
||
}
|
||
else if (SIM_FRV_FR0_REGNUM <= rn && rn <= SIM_FRV_FR63_REGNUM)
|
||
{
|
||
int hi_available, lo_available;
|
||
int frn = rn - SIM_FRV_FR0_REGNUM;
|
||
|
||
frv_fr_registers_available (current_cpu, &hi_available, &lo_available);
|
||
|
||
if ((frn < 32 && !lo_available) || (frn >= 32 && !hi_available))
|
||
return 0;
|
||
else
|
||
SETTSI (buf, GET_H_FR (frn));
|
||
}
|
||
else if (rn == SIM_FRV_PC_REGNUM)
|
||
SETTSI (buf, GET_H_PC ());
|
||
else if (SIM_FRV_SPR0_REGNUM <= rn && rn <= SIM_FRV_SPR4095_REGNUM)
|
||
{
|
||
/* Make sure the register is implemented. */
|
||
FRV_REGISTER_CONTROL *control = CPU_REGISTER_CONTROL (current_cpu);
|
||
int spr = rn - SIM_FRV_SPR0_REGNUM;
|
||
if (! control->spr[spr].implemented)
|
||
return 0;
|
||
SETTSI (buf, GET_H_SPR (spr));
|
||
}
|
||
else
|
||
{
|
||
SETTSI (buf, 0xdeadbeef);
|
||
return 0;
|
||
}
|
||
|
||
return len;
|
||
}
|
||
|
||
/* The contents of BUF are in target byte order. */
|
||
|
||
int
|
||
frvbf_store_register (SIM_CPU *current_cpu, int rn, const void *buf, int len)
|
||
{
|
||
if (SIM_FRV_GR0_REGNUM <= rn && rn <= SIM_FRV_GR63_REGNUM)
|
||
{
|
||
int hi_available, lo_available;
|
||
int grn = rn - SIM_FRV_GR0_REGNUM;
|
||
|
||
frv_gr_registers_available (current_cpu, &hi_available, &lo_available);
|
||
|
||
if ((grn < 32 && !lo_available) || (grn >= 32 && !hi_available))
|
||
return 0;
|
||
else
|
||
SET_H_GR (grn, GETTSI (buf));
|
||
}
|
||
else if (SIM_FRV_FR0_REGNUM <= rn && rn <= SIM_FRV_FR63_REGNUM)
|
||
{
|
||
int hi_available, lo_available;
|
||
int frn = rn - SIM_FRV_FR0_REGNUM;
|
||
|
||
frv_fr_registers_available (current_cpu, &hi_available, &lo_available);
|
||
|
||
if ((frn < 32 && !lo_available) || (frn >= 32 && !hi_available))
|
||
return 0;
|
||
else
|
||
SET_H_FR (frn, GETTSI (buf));
|
||
}
|
||
else if (rn == SIM_FRV_PC_REGNUM)
|
||
SET_H_PC (GETTSI (buf));
|
||
else if (SIM_FRV_SPR0_REGNUM <= rn && rn <= SIM_FRV_SPR4095_REGNUM)
|
||
{
|
||
/* Make sure the register is implemented. */
|
||
FRV_REGISTER_CONTROL *control = CPU_REGISTER_CONTROL (current_cpu);
|
||
int spr = rn - SIM_FRV_SPR0_REGNUM;
|
||
if (! control->spr[spr].implemented)
|
||
return 0;
|
||
SET_H_SPR (spr, GETTSI (buf));
|
||
}
|
||
else
|
||
return 0;
|
||
|
||
return len;
|
||
}
|
||
|
||
/* Cover fns to access the general registers. */
|
||
USI
|
||
frvbf_h_gr_get_handler (SIM_CPU *current_cpu, UINT gr)
|
||
{
|
||
frv_check_gr_access (current_cpu, gr);
|
||
return CPU (h_gr[gr]);
|
||
}
|
||
|
||
void
|
||
frvbf_h_gr_set_handler (SIM_CPU *current_cpu, UINT gr, USI newval)
|
||
{
|
||
frv_check_gr_access (current_cpu, gr);
|
||
|
||
if (gr == 0)
|
||
return; /* Storing into gr0 has no effect. */
|
||
|
||
CPU (h_gr[gr]) = newval;
|
||
}
|
||
|
||
/* Cover fns to access the floating point registers. */
|
||
SF
|
||
frvbf_h_fr_get_handler (SIM_CPU *current_cpu, UINT fr)
|
||
{
|
||
frv_check_fr_access (current_cpu, fr);
|
||
return CPU (h_fr[fr]);
|
||
}
|
||
|
||
void
|
||
frvbf_h_fr_set_handler (SIM_CPU *current_cpu, UINT fr, SF newval)
|
||
{
|
||
frv_check_fr_access (current_cpu, fr);
|
||
CPU (h_fr[fr]) = newval;
|
||
}
|
||
|
||
/* Cover fns to access the general registers as double words. */
|
||
static UINT
|
||
check_register_alignment (SIM_CPU *current_cpu, UINT reg, int align_mask)
|
||
{
|
||
if (reg & align_mask)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (current_cpu);
|
||
switch (STATE_ARCHITECTURE (sd)->mach)
|
||
{
|
||
/* Note: there is a discrepancy between V2.2 of the FR400
|
||
instruction manual and the various FR4xx LSI specs.
|
||
The former claims that unaligned registers cause a
|
||
register_exception while the latter say it's an
|
||
illegal_instruction. The LSI specs appear to be
|
||
correct; in fact, the FR4xx series is not documented
|
||
as having a register_exception. */
|
||
case bfd_mach_fr400:
|
||
case bfd_mach_fr450:
|
||
case bfd_mach_fr550:
|
||
frv_queue_program_interrupt (current_cpu, FRV_ILLEGAL_INSTRUCTION);
|
||
break;
|
||
case bfd_mach_frvtomcat:
|
||
case bfd_mach_fr500:
|
||
case bfd_mach_frv:
|
||
frv_queue_register_exception_interrupt (current_cpu,
|
||
FRV_REC_UNALIGNED);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
reg &= ~align_mask;
|
||
}
|
||
|
||
return reg;
|
||
}
|
||
|
||
static UINT
|
||
check_fr_register_alignment (SIM_CPU *current_cpu, UINT reg, int align_mask)
|
||
{
|
||
if (reg & align_mask)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (current_cpu);
|
||
switch (STATE_ARCHITECTURE (sd)->mach)
|
||
{
|
||
/* See comment in check_register_alignment(). */
|
||
case bfd_mach_fr400:
|
||
case bfd_mach_fr450:
|
||
case bfd_mach_fr550:
|
||
frv_queue_program_interrupt (current_cpu, FRV_ILLEGAL_INSTRUCTION);
|
||
break;
|
||
case bfd_mach_frvtomcat:
|
||
case bfd_mach_fr500:
|
||
case bfd_mach_frv:
|
||
{
|
||
struct frv_fp_exception_info fp_info = {
|
||
FSR_NO_EXCEPTION, FTT_INVALID_FR
|
||
};
|
||
frv_queue_fp_exception_interrupt (current_cpu, & fp_info);
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
reg &= ~align_mask;
|
||
}
|
||
|
||
return reg;
|
||
}
|
||
|
||
static UINT
|
||
check_memory_alignment (SIM_CPU *current_cpu, SI address, int align_mask)
|
||
{
|
||
if (address & align_mask)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (current_cpu);
|
||
switch (STATE_ARCHITECTURE (sd)->mach)
|
||
{
|
||
/* See comment in check_register_alignment(). */
|
||
case bfd_mach_fr400:
|
||
case bfd_mach_fr450:
|
||
frv_queue_data_access_error_interrupt (current_cpu, address);
|
||
break;
|
||
case bfd_mach_frvtomcat:
|
||
case bfd_mach_fr500:
|
||
case bfd_mach_frv:
|
||
frv_queue_mem_address_not_aligned_interrupt (current_cpu, address);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
address &= ~align_mask;
|
||
}
|
||
|
||
return address;
|
||
}
|
||
|
||
DI
|
||
frvbf_h_gr_double_get_handler (SIM_CPU *current_cpu, UINT gr)
|
||
{
|
||
DI value;
|
||
|
||
if (gr == 0)
|
||
return 0; /* gr0 is always 0. */
|
||
|
||
/* Check the register alignment. */
|
||
gr = check_register_alignment (current_cpu, gr, 1);
|
||
|
||
value = GET_H_GR (gr);
|
||
value <<= 32;
|
||
value |= (USI) GET_H_GR (gr + 1);
|
||
return value;
|
||
}
|
||
|
||
void
|
||
frvbf_h_gr_double_set_handler (SIM_CPU *current_cpu, UINT gr, DI newval)
|
||
{
|
||
if (gr == 0)
|
||
return; /* Storing into gr0 has no effect. */
|
||
|
||
/* Check the register alignment. */
|
||
gr = check_register_alignment (current_cpu, gr, 1);
|
||
|
||
SET_H_GR (gr , (newval >> 32) & 0xffffffff);
|
||
SET_H_GR (gr + 1, (newval ) & 0xffffffff);
|
||
}
|
||
|
||
/* Cover fns to access the floating point register as double words. */
|
||
DF
|
||
frvbf_h_fr_double_get_handler (SIM_CPU *current_cpu, UINT fr)
|
||
{
|
||
union {
|
||
SF as_sf[2];
|
||
DF as_df;
|
||
} value;
|
||
|
||
/* Check the register alignment. */
|
||
fr = check_fr_register_alignment (current_cpu, fr, 1);
|
||
|
||
if (HOST_BYTE_ORDER == BFD_ENDIAN_LITTLE)
|
||
{
|
||
value.as_sf[1] = GET_H_FR (fr);
|
||
value.as_sf[0] = GET_H_FR (fr + 1);
|
||
}
|
||
else
|
||
{
|
||
value.as_sf[0] = GET_H_FR (fr);
|
||
value.as_sf[1] = GET_H_FR (fr + 1);
|
||
}
|
||
|
||
return value.as_df;
|
||
}
|
||
|
||
void
|
||
frvbf_h_fr_double_set_handler (SIM_CPU *current_cpu, UINT fr, DF newval)
|
||
{
|
||
union {
|
||
SF as_sf[2];
|
||
DF as_df;
|
||
} value;
|
||
|
||
/* Check the register alignment. */
|
||
fr = check_fr_register_alignment (current_cpu, fr, 1);
|
||
|
||
value.as_df = newval;
|
||
if (HOST_BYTE_ORDER == BFD_ENDIAN_LITTLE)
|
||
{
|
||
SET_H_FR (fr , value.as_sf[1]);
|
||
SET_H_FR (fr + 1, value.as_sf[0]);
|
||
}
|
||
else
|
||
{
|
||
SET_H_FR (fr , value.as_sf[0]);
|
||
SET_H_FR (fr + 1, value.as_sf[1]);
|
||
}
|
||
}
|
||
|
||
/* Cover fns to access the floating point register as integer words. */
|
||
USI
|
||
frvbf_h_fr_int_get_handler (SIM_CPU *current_cpu, UINT fr)
|
||
{
|
||
union {
|
||
SF as_sf;
|
||
USI as_usi;
|
||
} value;
|
||
|
||
value.as_sf = GET_H_FR (fr);
|
||
return value.as_usi;
|
||
}
|
||
|
||
void
|
||
frvbf_h_fr_int_set_handler (SIM_CPU *current_cpu, UINT fr, USI newval)
|
||
{
|
||
union {
|
||
SF as_sf;
|
||
USI as_usi;
|
||
} value;
|
||
|
||
value.as_usi = newval;
|
||
SET_H_FR (fr, value.as_sf);
|
||
}
|
||
|
||
/* Cover fns to access the coprocessor registers as double words. */
|
||
DI
|
||
frvbf_h_cpr_double_get_handler (SIM_CPU *current_cpu, UINT cpr)
|
||
{
|
||
DI value;
|
||
|
||
/* Check the register alignment. */
|
||
cpr = check_register_alignment (current_cpu, cpr, 1);
|
||
|
||
value = GET_H_CPR (cpr);
|
||
value <<= 32;
|
||
value |= (USI) GET_H_CPR (cpr + 1);
|
||
return value;
|
||
}
|
||
|
||
void
|
||
frvbf_h_cpr_double_set_handler (SIM_CPU *current_cpu, UINT cpr, DI newval)
|
||
{
|
||
/* Check the register alignment. */
|
||
cpr = check_register_alignment (current_cpu, cpr, 1);
|
||
|
||
SET_H_CPR (cpr , (newval >> 32) & 0xffffffff);
|
||
SET_H_CPR (cpr + 1, (newval ) & 0xffffffff);
|
||
}
|
||
|
||
/* Cover fns to write registers as quad words. */
|
||
void
|
||
frvbf_h_gr_quad_set_handler (SIM_CPU *current_cpu, UINT gr, SI *newval)
|
||
{
|
||
if (gr == 0)
|
||
return; /* Storing into gr0 has no effect. */
|
||
|
||
/* Check the register alignment. */
|
||
gr = check_register_alignment (current_cpu, gr, 3);
|
||
|
||
SET_H_GR (gr , newval[0]);
|
||
SET_H_GR (gr + 1, newval[1]);
|
||
SET_H_GR (gr + 2, newval[2]);
|
||
SET_H_GR (gr + 3, newval[3]);
|
||
}
|
||
|
||
void
|
||
frvbf_h_fr_quad_set_handler (SIM_CPU *current_cpu, UINT fr, SI *newval)
|
||
{
|
||
/* Check the register alignment. */
|
||
fr = check_fr_register_alignment (current_cpu, fr, 3);
|
||
|
||
SET_H_FR (fr , newval[0]);
|
||
SET_H_FR (fr + 1, newval[1]);
|
||
SET_H_FR (fr + 2, newval[2]);
|
||
SET_H_FR (fr + 3, newval[3]);
|
||
}
|
||
|
||
void
|
||
frvbf_h_cpr_quad_set_handler (SIM_CPU *current_cpu, UINT cpr, SI *newval)
|
||
{
|
||
/* Check the register alignment. */
|
||
cpr = check_register_alignment (current_cpu, cpr, 3);
|
||
|
||
SET_H_CPR (cpr , newval[0]);
|
||
SET_H_CPR (cpr + 1, newval[1]);
|
||
SET_H_CPR (cpr + 2, newval[2]);
|
||
SET_H_CPR (cpr + 3, newval[3]);
|
||
}
|
||
|
||
/* Cover fns to access the special purpose registers. */
|
||
USI
|
||
frvbf_h_spr_get_handler (SIM_CPU *current_cpu, UINT spr)
|
||
{
|
||
/* Check access restrictions. */
|
||
frv_check_spr_read_access (current_cpu, spr);
|
||
|
||
switch (spr)
|
||
{
|
||
case H_SPR_PSR:
|
||
return spr_psr_get_handler (current_cpu);
|
||
case H_SPR_TBR:
|
||
return spr_tbr_get_handler (current_cpu);
|
||
case H_SPR_BPSR:
|
||
return spr_bpsr_get_handler (current_cpu);
|
||
case H_SPR_CCR:
|
||
return spr_ccr_get_handler (current_cpu);
|
||
case H_SPR_CCCR:
|
||
return spr_cccr_get_handler (current_cpu);
|
||
case H_SPR_SR0:
|
||
case H_SPR_SR1:
|
||
case H_SPR_SR2:
|
||
case H_SPR_SR3:
|
||
return spr_sr_get_handler (current_cpu, spr);
|
||
break;
|
||
default:
|
||
return CPU (h_spr[spr]);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
void
|
||
frvbf_h_spr_set_handler (SIM_CPU *current_cpu, UINT spr, USI newval)
|
||
{
|
||
FRV_REGISTER_CONTROL *control;
|
||
USI mask;
|
||
USI oldval;
|
||
|
||
/* Check access restrictions. */
|
||
frv_check_spr_write_access (current_cpu, spr);
|
||
|
||
/* Only set those fields which are writeable. */
|
||
control = CPU_REGISTER_CONTROL (current_cpu);
|
||
mask = control->spr[spr].read_only_mask;
|
||
oldval = GET_H_SPR (spr);
|
||
|
||
newval = (newval & ~mask) | (oldval & mask);
|
||
|
||
/* Some registers are represented by individual components which are
|
||
referenced more often than the register itself. */
|
||
switch (spr)
|
||
{
|
||
case H_SPR_PSR:
|
||
spr_psr_set_handler (current_cpu, newval);
|
||
break;
|
||
case H_SPR_TBR:
|
||
spr_tbr_set_handler (current_cpu, newval);
|
||
break;
|
||
case H_SPR_BPSR:
|
||
spr_bpsr_set_handler (current_cpu, newval);
|
||
break;
|
||
case H_SPR_CCR:
|
||
spr_ccr_set_handler (current_cpu, newval);
|
||
break;
|
||
case H_SPR_CCCR:
|
||
spr_cccr_set_handler (current_cpu, newval);
|
||
break;
|
||
case H_SPR_SR0:
|
||
case H_SPR_SR1:
|
||
case H_SPR_SR2:
|
||
case H_SPR_SR3:
|
||
spr_sr_set_handler (current_cpu, spr, newval);
|
||
break;
|
||
case H_SPR_IHSR8:
|
||
frv_cache_reconfigure (current_cpu, CPU_INSN_CACHE (current_cpu));
|
||
break;
|
||
default:
|
||
CPU (h_spr[spr]) = newval;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Cover fns to access the gr_hi and gr_lo registers. */
|
||
UHI
|
||
frvbf_h_gr_hi_get_handler (SIM_CPU *current_cpu, UINT gr)
|
||
{
|
||
return (GET_H_GR(gr) >> 16) & 0xffff;
|
||
}
|
||
|
||
void
|
||
frvbf_h_gr_hi_set_handler (SIM_CPU *current_cpu, UINT gr, UHI newval)
|
||
{
|
||
USI value = (GET_H_GR (gr) & 0xffff) | (newval << 16);
|
||
SET_H_GR (gr, value);
|
||
}
|
||
|
||
UHI
|
||
frvbf_h_gr_lo_get_handler (SIM_CPU *current_cpu, UINT gr)
|
||
{
|
||
return GET_H_GR(gr) & 0xffff;
|
||
}
|
||
|
||
void
|
||
frvbf_h_gr_lo_set_handler (SIM_CPU *current_cpu, UINT gr, UHI newval)
|
||
{
|
||
USI value = (GET_H_GR (gr) & 0xffff0000) | (newval & 0xffff);
|
||
SET_H_GR (gr, value);
|
||
}
|
||
|
||
/* Cover fns to access the tbr bits. */
|
||
USI
|
||
spr_tbr_get_handler (SIM_CPU *current_cpu)
|
||
{
|
||
int tbr = ((GET_H_TBR_TBA () & 0xfffff) << 12) |
|
||
((GET_H_TBR_TT () & 0xff) << 4);
|
||
|
||
return tbr;
|
||
}
|
||
|
||
void
|
||
spr_tbr_set_handler (SIM_CPU *current_cpu, USI newval)
|
||
{
|
||
int tbr = newval;
|
||
|
||
SET_H_TBR_TBA ((tbr >> 12) & 0xfffff) ;
|
||
SET_H_TBR_TT ((tbr >> 4) & 0xff) ;
|
||
}
|
||
|
||
/* Cover fns to access the bpsr bits. */
|
||
USI
|
||
spr_bpsr_get_handler (SIM_CPU *current_cpu)
|
||
{
|
||
int bpsr = ((GET_H_BPSR_BS () & 0x1) << 12) |
|
||
((GET_H_BPSR_BET () & 0x1) );
|
||
|
||
return bpsr;
|
||
}
|
||
|
||
void
|
||
spr_bpsr_set_handler (SIM_CPU *current_cpu, USI newval)
|
||
{
|
||
int bpsr = newval;
|
||
|
||
SET_H_BPSR_BS ((bpsr >> 12) & 1);
|
||
SET_H_BPSR_BET ((bpsr ) & 1);
|
||
}
|
||
|
||
/* Cover fns to access the psr bits. */
|
||
USI
|
||
spr_psr_get_handler (SIM_CPU *current_cpu)
|
||
{
|
||
int psr = ((GET_H_PSR_IMPLE () & 0xf) << 28) |
|
||
((GET_H_PSR_VER () & 0xf) << 24) |
|
||
((GET_H_PSR_ICE () & 0x1) << 16) |
|
||
((GET_H_PSR_NEM () & 0x1) << 14) |
|
||
((GET_H_PSR_CM () & 0x1) << 13) |
|
||
((GET_H_PSR_BE () & 0x1) << 12) |
|
||
((GET_H_PSR_ESR () & 0x1) << 11) |
|
||
((GET_H_PSR_EF () & 0x1) << 8) |
|
||
((GET_H_PSR_EM () & 0x1) << 7) |
|
||
((GET_H_PSR_PIL () & 0xf) << 3) |
|
||
((GET_H_PSR_S () & 0x1) << 2) |
|
||
((GET_H_PSR_PS () & 0x1) << 1) |
|
||
((GET_H_PSR_ET () & 0x1) );
|
||
|
||
return psr;
|
||
}
|
||
|
||
void
|
||
spr_psr_set_handler (SIM_CPU *current_cpu, USI newval)
|
||
{
|
||
/* The handler for PSR.S references the value of PSR.ESR, so set PSR.S
|
||
first. */
|
||
SET_H_PSR_S ((newval >> 2) & 1);
|
||
|
||
SET_H_PSR_IMPLE ((newval >> 28) & 0xf);
|
||
SET_H_PSR_VER ((newval >> 24) & 0xf);
|
||
SET_H_PSR_ICE ((newval >> 16) & 1);
|
||
SET_H_PSR_NEM ((newval >> 14) & 1);
|
||
SET_H_PSR_CM ((newval >> 13) & 1);
|
||
SET_H_PSR_BE ((newval >> 12) & 1);
|
||
SET_H_PSR_ESR ((newval >> 11) & 1);
|
||
SET_H_PSR_EF ((newval >> 8) & 1);
|
||
SET_H_PSR_EM ((newval >> 7) & 1);
|
||
SET_H_PSR_PIL ((newval >> 3) & 0xf);
|
||
SET_H_PSR_PS ((newval >> 1) & 1);
|
||
SET_H_PSR_ET ((newval ) & 1);
|
||
}
|
||
|
||
void
|
||
frvbf_h_psr_s_set_handler (SIM_CPU *current_cpu, BI newval)
|
||
{
|
||
/* If switching from user to supervisor mode, or vice-versa, then switch
|
||
the supervisor/user context. */
|
||
int psr_s = GET_H_PSR_S ();
|
||
if (psr_s != (newval & 1))
|
||
{
|
||
frvbf_switch_supervisor_user_context (current_cpu);
|
||
CPU (h_psr_s) = newval & 1;
|
||
}
|
||
}
|
||
|
||
/* Cover fns to access the ccr bits. */
|
||
USI
|
||
spr_ccr_get_handler (SIM_CPU *current_cpu)
|
||
{
|
||
int ccr = ((GET_H_ICCR (H_ICCR_ICC3) & 0xf) << 28) |
|
||
((GET_H_ICCR (H_ICCR_ICC2) & 0xf) << 24) |
|
||
((GET_H_ICCR (H_ICCR_ICC1) & 0xf) << 20) |
|
||
((GET_H_ICCR (H_ICCR_ICC0) & 0xf) << 16) |
|
||
((GET_H_FCCR (H_FCCR_FCC3) & 0xf) << 12) |
|
||
((GET_H_FCCR (H_FCCR_FCC2) & 0xf) << 8) |
|
||
((GET_H_FCCR (H_FCCR_FCC1) & 0xf) << 4) |
|
||
((GET_H_FCCR (H_FCCR_FCC0) & 0xf) );
|
||
|
||
return ccr;
|
||
}
|
||
|
||
void
|
||
spr_ccr_set_handler (SIM_CPU *current_cpu, USI newval)
|
||
{
|
||
SET_H_ICCR (H_ICCR_ICC3, (newval >> 28) & 0xf);
|
||
SET_H_ICCR (H_ICCR_ICC2, (newval >> 24) & 0xf);
|
||
SET_H_ICCR (H_ICCR_ICC1, (newval >> 20) & 0xf);
|
||
SET_H_ICCR (H_ICCR_ICC0, (newval >> 16) & 0xf);
|
||
SET_H_FCCR (H_FCCR_FCC3, (newval >> 12) & 0xf);
|
||
SET_H_FCCR (H_FCCR_FCC2, (newval >> 8) & 0xf);
|
||
SET_H_FCCR (H_FCCR_FCC1, (newval >> 4) & 0xf);
|
||
SET_H_FCCR (H_FCCR_FCC0, (newval ) & 0xf);
|
||
}
|
||
|
||
QI
|
||
frvbf_set_icc_for_shift_right (
|
||
SIM_CPU *current_cpu, SI value, SI shift, QI icc
|
||
)
|
||
{
|
||
/* Set the C flag of the given icc to the logical OR of the bits shifted
|
||
out. */
|
||
int mask = (1 << shift) - 1;
|
||
if ((value & mask) != 0)
|
||
return icc | 0x1;
|
||
|
||
return icc & 0xe;
|
||
}
|
||
|
||
QI
|
||
frvbf_set_icc_for_shift_left (
|
||
SIM_CPU *current_cpu, SI value, SI shift, QI icc
|
||
)
|
||
{
|
||
/* Set the V flag of the given icc to the logical OR of the bits shifted
|
||
out. */
|
||
int mask = ((1 << shift) - 1) << (32 - shift);
|
||
if ((value & mask) != 0)
|
||
return icc | 0x2;
|
||
|
||
return icc & 0xd;
|
||
}
|
||
|
||
/* Cover fns to access the cccr bits. */
|
||
USI
|
||
spr_cccr_get_handler (SIM_CPU *current_cpu)
|
||
{
|
||
int cccr = ((GET_H_CCCR (H_CCCR_CC7) & 0x3) << 14) |
|
||
((GET_H_CCCR (H_CCCR_CC6) & 0x3) << 12) |
|
||
((GET_H_CCCR (H_CCCR_CC5) & 0x3) << 10) |
|
||
((GET_H_CCCR (H_CCCR_CC4) & 0x3) << 8) |
|
||
((GET_H_CCCR (H_CCCR_CC3) & 0x3) << 6) |
|
||
((GET_H_CCCR (H_CCCR_CC2) & 0x3) << 4) |
|
||
((GET_H_CCCR (H_CCCR_CC1) & 0x3) << 2) |
|
||
((GET_H_CCCR (H_CCCR_CC0) & 0x3) );
|
||
|
||
return cccr;
|
||
}
|
||
|
||
void
|
||
spr_cccr_set_handler (SIM_CPU *current_cpu, USI newval)
|
||
{
|
||
SET_H_CCCR (H_CCCR_CC7, (newval >> 14) & 0x3);
|
||
SET_H_CCCR (H_CCCR_CC6, (newval >> 12) & 0x3);
|
||
SET_H_CCCR (H_CCCR_CC5, (newval >> 10) & 0x3);
|
||
SET_H_CCCR (H_CCCR_CC4, (newval >> 8) & 0x3);
|
||
SET_H_CCCR (H_CCCR_CC3, (newval >> 6) & 0x3);
|
||
SET_H_CCCR (H_CCCR_CC2, (newval >> 4) & 0x3);
|
||
SET_H_CCCR (H_CCCR_CC1, (newval >> 2) & 0x3);
|
||
SET_H_CCCR (H_CCCR_CC0, (newval ) & 0x3);
|
||
}
|
||
|
||
/* Cover fns to access the sr bits. */
|
||
USI
|
||
spr_sr_get_handler (SIM_CPU *current_cpu, UINT spr)
|
||
{
|
||
/* If PSR.ESR is not set, then SR0-3 map onto SGR4-7 which will be GR4-7,
|
||
otherwise the correct mapping of USG4-7 or SGR4-7 will be in SR0-3. */
|
||
int psr_esr = GET_H_PSR_ESR ();
|
||
if (! psr_esr)
|
||
return GET_H_GR (4 + (spr - H_SPR_SR0));
|
||
|
||
return CPU (h_spr[spr]);
|
||
}
|
||
|
||
void
|
||
spr_sr_set_handler (SIM_CPU *current_cpu, UINT spr, USI newval)
|
||
{
|
||
/* If PSR.ESR is not set, then SR0-3 map onto SGR4-7 which will be GR4-7,
|
||
otherwise the correct mapping of USG4-7 or SGR4-7 will be in SR0-3. */
|
||
int psr_esr = GET_H_PSR_ESR ();
|
||
if (! psr_esr)
|
||
SET_H_GR (4 + (spr - H_SPR_SR0), newval);
|
||
else
|
||
CPU (h_spr[spr]) = newval;
|
||
}
|
||
|
||
/* Switch SR0-SR4 with GR4-GR7 if PSR.ESR is set. */
|
||
void
|
||
frvbf_switch_supervisor_user_context (SIM_CPU *current_cpu)
|
||
{
|
||
if (GET_H_PSR_ESR ())
|
||
{
|
||
/* We need to be in supervisor mode to swap the registers. Access the
|
||
PSR.S directly in order to avoid recursive context switches. */
|
||
int i;
|
||
int save_psr_s = CPU (h_psr_s);
|
||
CPU (h_psr_s) = 1;
|
||
for (i = 0; i < 4; ++i)
|
||
{
|
||
int gr = i + 4;
|
||
int spr = i + H_SPR_SR0;
|
||
SI tmp = GET_H_SPR (spr);
|
||
SET_H_SPR (spr, GET_H_GR (gr));
|
||
SET_H_GR (gr, tmp);
|
||
}
|
||
CPU (h_psr_s) = save_psr_s;
|
||
}
|
||
}
|
||
|
||
/* Handle load/store of quad registers. */
|
||
void
|
||
frvbf_load_quad_GR (SIM_CPU *current_cpu, PCADDR pc, SI address, SI targ_ix)
|
||
{
|
||
int i;
|
||
SI value[4];
|
||
|
||
/* Check memory alignment */
|
||
address = check_memory_alignment (current_cpu, address, 0xf);
|
||
|
||
/* If we need to count cycles, then the cache operation will be
|
||
initiated from the model profiling functions.
|
||
See frvbf_model_.... */
|
||
if (model_insn)
|
||
{
|
||
CPU_LOAD_ADDRESS (current_cpu) = address;
|
||
CPU_LOAD_LENGTH (current_cpu) = 16;
|
||
}
|
||
else
|
||
{
|
||
for (i = 0; i < 4; ++i)
|
||
{
|
||
value[i] = frvbf_read_mem_SI (current_cpu, pc, address);
|
||
address += 4;
|
||
}
|
||
sim_queue_fn_xi_write (current_cpu, frvbf_h_gr_quad_set_handler, targ_ix,
|
||
value);
|
||
}
|
||
}
|
||
|
||
void
|
||
frvbf_store_quad_GR (SIM_CPU *current_cpu, PCADDR pc, SI address, SI src_ix)
|
||
{
|
||
int i;
|
||
SI value[4];
|
||
USI hsr0;
|
||
|
||
/* Check register and memory alignment. */
|
||
src_ix = check_register_alignment (current_cpu, src_ix, 3);
|
||
address = check_memory_alignment (current_cpu, address, 0xf);
|
||
|
||
for (i = 0; i < 4; ++i)
|
||
{
|
||
/* GR0 is always 0. */
|
||
if (src_ix == 0)
|
||
value[i] = 0;
|
||
else
|
||
value[i] = GET_H_GR (src_ix + i);
|
||
}
|
||
hsr0 = GET_HSR0 ();
|
||
if (GET_HSR0_DCE (hsr0))
|
||
sim_queue_fn_mem_xi_write (current_cpu, frvbf_mem_set_XI, address, value);
|
||
else
|
||
sim_queue_mem_xi_write (current_cpu, address, value);
|
||
}
|
||
|
||
void
|
||
frvbf_load_quad_FRint (SIM_CPU *current_cpu, PCADDR pc, SI address, SI targ_ix)
|
||
{
|
||
int i;
|
||
SI value[4];
|
||
|
||
/* Check memory alignment */
|
||
address = check_memory_alignment (current_cpu, address, 0xf);
|
||
|
||
/* If we need to count cycles, then the cache operation will be
|
||
initiated from the model profiling functions.
|
||
See frvbf_model_.... */
|
||
if (model_insn)
|
||
{
|
||
CPU_LOAD_ADDRESS (current_cpu) = address;
|
||
CPU_LOAD_LENGTH (current_cpu) = 16;
|
||
}
|
||
else
|
||
{
|
||
for (i = 0; i < 4; ++i)
|
||
{
|
||
value[i] = frvbf_read_mem_SI (current_cpu, pc, address);
|
||
address += 4;
|
||
}
|
||
sim_queue_fn_xi_write (current_cpu, frvbf_h_fr_quad_set_handler, targ_ix,
|
||
value);
|
||
}
|
||
}
|
||
|
||
void
|
||
frvbf_store_quad_FRint (SIM_CPU *current_cpu, PCADDR pc, SI address, SI src_ix)
|
||
{
|
||
int i;
|
||
SI value[4];
|
||
USI hsr0;
|
||
|
||
/* Check register and memory alignment. */
|
||
src_ix = check_fr_register_alignment (current_cpu, src_ix, 3);
|
||
address = check_memory_alignment (current_cpu, address, 0xf);
|
||
|
||
for (i = 0; i < 4; ++i)
|
||
value[i] = GET_H_FR (src_ix + i);
|
||
|
||
hsr0 = GET_HSR0 ();
|
||
if (GET_HSR0_DCE (hsr0))
|
||
sim_queue_fn_mem_xi_write (current_cpu, frvbf_mem_set_XI, address, value);
|
||
else
|
||
sim_queue_mem_xi_write (current_cpu, address, value);
|
||
}
|
||
|
||
void
|
||
frvbf_load_quad_CPR (SIM_CPU *current_cpu, PCADDR pc, SI address, SI targ_ix)
|
||
{
|
||
int i;
|
||
SI value[4];
|
||
|
||
/* Check memory alignment */
|
||
address = check_memory_alignment (current_cpu, address, 0xf);
|
||
|
||
/* If we need to count cycles, then the cache operation will be
|
||
initiated from the model profiling functions.
|
||
See frvbf_model_.... */
|
||
if (model_insn)
|
||
{
|
||
CPU_LOAD_ADDRESS (current_cpu) = address;
|
||
CPU_LOAD_LENGTH (current_cpu) = 16;
|
||
}
|
||
else
|
||
{
|
||
for (i = 0; i < 4; ++i)
|
||
{
|
||
value[i] = frvbf_read_mem_SI (current_cpu, pc, address);
|
||
address += 4;
|
||
}
|
||
sim_queue_fn_xi_write (current_cpu, frvbf_h_cpr_quad_set_handler, targ_ix,
|
||
value);
|
||
}
|
||
}
|
||
|
||
void
|
||
frvbf_store_quad_CPR (SIM_CPU *current_cpu, PCADDR pc, SI address, SI src_ix)
|
||
{
|
||
int i;
|
||
SI value[4];
|
||
USI hsr0;
|
||
|
||
/* Check register and memory alignment. */
|
||
src_ix = check_register_alignment (current_cpu, src_ix, 3);
|
||
address = check_memory_alignment (current_cpu, address, 0xf);
|
||
|
||
for (i = 0; i < 4; ++i)
|
||
value[i] = GET_H_CPR (src_ix + i);
|
||
|
||
hsr0 = GET_HSR0 ();
|
||
if (GET_HSR0_DCE (hsr0))
|
||
sim_queue_fn_mem_xi_write (current_cpu, frvbf_mem_set_XI, address, value);
|
||
else
|
||
sim_queue_mem_xi_write (current_cpu, address, value);
|
||
}
|
||
|
||
void
|
||
frvbf_signed_integer_divide (
|
||
SIM_CPU *current_cpu, SI arg1, SI arg2, int target_index, int non_excepting
|
||
)
|
||
{
|
||
enum frv_dtt dtt = FRV_DTT_NO_EXCEPTION;
|
||
if (arg1 == 0x80000000 && arg2 == -1)
|
||
{
|
||
/* 0x80000000/(-1) must result in 0x7fffffff when ISR.EDE is set
|
||
otherwise it may result in 0x7fffffff (sparc compatibility) or
|
||
0x80000000 (C language compatibility). */
|
||
USI isr;
|
||
dtt = FRV_DTT_OVERFLOW;
|
||
|
||
isr = GET_ISR ();
|
||
if (GET_ISR_EDE (isr))
|
||
sim_queue_fn_si_write (current_cpu, frvbf_h_gr_set, target_index,
|
||
0x7fffffff);
|
||
else
|
||
sim_queue_fn_si_write (current_cpu, frvbf_h_gr_set, target_index,
|
||
0x80000000);
|
||
frvbf_force_update (current_cpu); /* Force update of target register. */
|
||
}
|
||
else if (arg2 == 0)
|
||
dtt = FRV_DTT_DIVISION_BY_ZERO;
|
||
else
|
||
sim_queue_fn_si_write (current_cpu, frvbf_h_gr_set, target_index,
|
||
arg1 / arg2);
|
||
|
||
/* Check for exceptions. */
|
||
if (dtt != FRV_DTT_NO_EXCEPTION)
|
||
dtt = frvbf_division_exception (current_cpu, dtt, target_index,
|
||
non_excepting);
|
||
if (non_excepting && dtt == FRV_DTT_NO_EXCEPTION)
|
||
{
|
||
/* Non excepting instruction. Clear the NE flag for the target
|
||
register. */
|
||
SI NE_flags[2];
|
||
GET_NE_FLAGS (NE_flags, H_SPR_GNER0);
|
||
CLEAR_NE_FLAG (NE_flags, target_index);
|
||
SET_NE_FLAGS (H_SPR_GNER0, NE_flags);
|
||
}
|
||
}
|
||
|
||
void
|
||
frvbf_unsigned_integer_divide (
|
||
SIM_CPU *current_cpu, USI arg1, USI arg2, int target_index, int non_excepting
|
||
)
|
||
{
|
||
if (arg2 == 0)
|
||
frvbf_division_exception (current_cpu, FRV_DTT_DIVISION_BY_ZERO,
|
||
target_index, non_excepting);
|
||
else
|
||
{
|
||
sim_queue_fn_si_write (current_cpu, frvbf_h_gr_set, target_index,
|
||
arg1 / arg2);
|
||
if (non_excepting)
|
||
{
|
||
/* Non excepting instruction. Clear the NE flag for the target
|
||
register. */
|
||
SI NE_flags[2];
|
||
GET_NE_FLAGS (NE_flags, H_SPR_GNER0);
|
||
CLEAR_NE_FLAG (NE_flags, target_index);
|
||
SET_NE_FLAGS (H_SPR_GNER0, NE_flags);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Clear accumulators. */
|
||
void
|
||
frvbf_clear_accumulators (SIM_CPU *current_cpu, SI acc_ix, int A)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (current_cpu);
|
||
int acc_mask =
|
||
(STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr500) ? 7 :
|
||
(STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr550) ? 7 :
|
||
(STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr450) ? 11 :
|
||
(STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr400) ? 3 :
|
||
63;
|
||
FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (current_cpu);
|
||
|
||
ps->mclracc_acc = acc_ix;
|
||
ps->mclracc_A = A;
|
||
if (A == 0 || acc_ix != 0) /* Clear 1 accumuator? */
|
||
{
|
||
/* This instruction is a nop if the referenced accumulator is not
|
||
implemented. */
|
||
if ((acc_ix & acc_mask) == acc_ix)
|
||
sim_queue_fn_di_write (current_cpu, frvbf_h_acc40S_set, acc_ix, 0);
|
||
}
|
||
else
|
||
{
|
||
/* Clear all implemented accumulators. */
|
||
int i;
|
||
for (i = 0; i <= acc_mask; ++i)
|
||
if ((i & acc_mask) == i)
|
||
sim_queue_fn_di_write (current_cpu, frvbf_h_acc40S_set, i, 0);
|
||
}
|
||
}
|
||
|
||
/* Functions to aid insn semantics. */
|
||
|
||
/* Compute the result of the SCAN and SCANI insns after the shift and xor. */
|
||
SI
|
||
frvbf_scan_result (SIM_CPU *current_cpu, SI value)
|
||
{
|
||
SI i;
|
||
SI mask;
|
||
|
||
if (value == 0)
|
||
return 63;
|
||
|
||
/* Find the position of the first non-zero bit.
|
||
The loop will terminate since there is guaranteed to be at least one
|
||
non-zero bit. */
|
||
mask = 1 << (sizeof (mask) * 8 - 1);
|
||
for (i = 0; (value & mask) == 0; ++i)
|
||
value <<= 1;
|
||
|
||
return i;
|
||
}
|
||
|
||
/* Compute the result of the cut insns. */
|
||
SI
|
||
frvbf_cut (SIM_CPU *current_cpu, SI reg1, SI reg2, SI cut_point)
|
||
{
|
||
SI result;
|
||
cut_point &= 0x3f;
|
||
if (cut_point < 32)
|
||
{
|
||
result = reg1 << cut_point;
|
||
result |= (reg2 >> (32 - cut_point)) & ((1 << cut_point) - 1);
|
||
}
|
||
else
|
||
result = reg2 << (cut_point - 32);
|
||
|
||
return result;
|
||
}
|
||
|
||
/* Compute the result of the cut insns. */
|
||
SI
|
||
frvbf_media_cut (SIM_CPU *current_cpu, DI acc, SI cut_point)
|
||
{
|
||
/* The cut point is the lower 6 bits (signed) of what we are passed. */
|
||
cut_point = cut_point << 26 >> 26;
|
||
|
||
/* The cut_point is relative to bit 40 of 64 bits. */
|
||
if (cut_point >= 0)
|
||
return (acc << (cut_point + 24)) >> 32;
|
||
|
||
/* Extend the sign bit (bit 40) for negative cuts. */
|
||
if (cut_point == -32)
|
||
return (acc << 24) >> 63; /* Special case for full shiftout. */
|
||
|
||
return (acc << 24) >> (32 + -cut_point);
|
||
}
|
||
|
||
/* Compute the result of the cut insns. */
|
||
SI
|
||
frvbf_media_cut_ss (SIM_CPU *current_cpu, DI acc, SI cut_point)
|
||
{
|
||
/* The cut point is the lower 6 bits (signed) of what we are passed. */
|
||
cut_point = cut_point << 26 >> 26;
|
||
|
||
if (cut_point >= 0)
|
||
{
|
||
/* The cut_point is relative to bit 40 of 64 bits. */
|
||
DI shifted = acc << (cut_point + 24);
|
||
DI unshifted = shifted >> (cut_point + 24);
|
||
|
||
/* The result will be saturated if significant bits are shifted out. */
|
||
if (unshifted != acc)
|
||
{
|
||
if (acc < 0)
|
||
return 0x80000000;
|
||
return 0x7fffffff;
|
||
}
|
||
}
|
||
|
||
/* The result will not be saturated, so use the code for the normal cut. */
|
||
return frvbf_media_cut (current_cpu, acc, cut_point);
|
||
}
|
||
|
||
/* Compute the result of int accumulator cut (SCUTSS). */
|
||
SI
|
||
frvbf_iacc_cut (SIM_CPU *current_cpu, DI acc, SI cut_point)
|
||
{
|
||
DI lower, upper;
|
||
|
||
/* The cut point is the lower 7 bits (signed) of what we are passed. */
|
||
cut_point = cut_point << 25 >> 25;
|
||
|
||
/* Conceptually, the operation is on a 128-bit sign-extension of ACC.
|
||
The top bit of the return value corresponds to bit (63 - CUT_POINT)
|
||
of this 128-bit value.
|
||
|
||
Since we can't deal with 128-bit values very easily, convert the
|
||
operation into an equivalent 64-bit one. */
|
||
if (cut_point < 0)
|
||
{
|
||
/* Avoid an undefined shift operation. */
|
||
if (cut_point == -64)
|
||
acc >>= 63;
|
||
else
|
||
acc >>= -cut_point;
|
||
cut_point = 0;
|
||
}
|
||
|
||
/* Get the shifted but unsaturated result. Set LOWER to the lowest
|
||
32 bits of the result and UPPER to the result >> 31. */
|
||
if (cut_point < 32)
|
||
{
|
||
/* The cut loses the (32 - CUT_POINT) least significant bits.
|
||
Round the result up if the most significant of these lost bits
|
||
is 1. */
|
||
lower = acc >> (32 - cut_point);
|
||
if (lower < 0x7fffffff)
|
||
if (acc & LSBIT64 (32 - cut_point - 1))
|
||
lower++;
|
||
upper = lower >> 31;
|
||
}
|
||
else
|
||
{
|
||
lower = acc << (cut_point - 32);
|
||
upper = acc >> (63 - cut_point);
|
||
}
|
||
|
||
/* Saturate the result. */
|
||
if (upper < -1)
|
||
return ~0x7fffffff;
|
||
else if (upper > 0)
|
||
return 0x7fffffff;
|
||
else
|
||
return lower;
|
||
}
|
||
|
||
/* Compute the result of shift-left-arithmetic-with-saturation (SLASS). */
|
||
SI
|
||
frvbf_shift_left_arith_saturate (SIM_CPU *current_cpu, SI arg1, SI arg2)
|
||
{
|
||
int neg_arg1;
|
||
|
||
/* FIXME: what to do with negative shift amt? */
|
||
if (arg2 <= 0)
|
||
return arg1;
|
||
|
||
if (arg1 == 0)
|
||
return 0;
|
||
|
||
/* Signed shift by 31 or greater saturates by definition. */
|
||
if (arg2 >= 31)
|
||
{
|
||
if (arg1 > 0)
|
||
return (SI) 0x7fffffff;
|
||
else
|
||
return (SI) 0x80000000;
|
||
}
|
||
|
||
/* OK, arg2 is between 1 and 31. */
|
||
neg_arg1 = (arg1 < 0);
|
||
do {
|
||
arg1 <<= 1;
|
||
/* Check for sign bit change (saturation). */
|
||
if (neg_arg1 && (arg1 >= 0))
|
||
return (SI) 0x80000000;
|
||
else if (!neg_arg1 && (arg1 < 0))
|
||
return (SI) 0x7fffffff;
|
||
} while (--arg2 > 0);
|
||
|
||
return arg1;
|
||
}
|
||
|
||
/* Simulate the media custom insns. */
|
||
void
|
||
frvbf_media_cop (SIM_CPU *current_cpu, int cop_num)
|
||
{
|
||
/* The semantics of the insn are a nop, since it is implementation defined.
|
||
We do need to check whether it's implemented and set up for MTRAP
|
||
if it's not. */
|
||
USI msr0 = GET_MSR (0);
|
||
if (GET_MSR_EMCI (msr0) == 0)
|
||
{
|
||
/* no interrupt queued at this time. */
|
||
frv_set_mp_exception_registers (current_cpu, MTT_UNIMPLEMENTED_MPOP, 0);
|
||
}
|
||
}
|
||
|
||
/* Simulate the media average (MAVEH) insn. */
|
||
static HI
|
||
do_media_average (SIM_CPU *current_cpu, HI arg1, HI arg2)
|
||
{
|
||
SIM_DESC sd = CPU_STATE (current_cpu);
|
||
SI sum = (arg1 + arg2);
|
||
HI result = sum >> 1;
|
||
int rounding_value;
|
||
|
||
/* On fr4xx and fr550, check the rounding mode. On other machines
|
||
rounding is always toward negative infinity and the result is
|
||
already correctly rounded. */
|
||
switch (STATE_ARCHITECTURE (sd)->mach)
|
||
{
|
||
/* Need to check rounding mode. */
|
||
case bfd_mach_fr400:
|
||
case bfd_mach_fr450:
|
||
case bfd_mach_fr550:
|
||
/* Check whether rounding will be required. Rounding will be required
|
||
if the sum is an odd number. */
|
||
rounding_value = sum & 1;
|
||
if (rounding_value)
|
||
{
|
||
USI msr0 = GET_MSR (0);
|
||
/* Check MSR0.SRDAV to determine which bits control the rounding. */
|
||
if (GET_MSR_SRDAV (msr0))
|
||
{
|
||
/* MSR0.RD controls rounding. */
|
||
switch (GET_MSR_RD (msr0))
|
||
{
|
||
case 0:
|
||
/* Round to nearest. */
|
||
if (result >= 0)
|
||
++result;
|
||
break;
|
||
case 1:
|
||
/* Round toward 0. */
|
||
if (result < 0)
|
||
++result;
|
||
break;
|
||
case 2:
|
||
/* Round toward positive infinity. */
|
||
++result;
|
||
break;
|
||
case 3:
|
||
/* Round toward negative infinity. The result is already
|
||
correctly rounded. */
|
||
break;
|
||
default:
|
||
abort ();
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* MSR0.RDAV controls rounding. If set, round toward positive
|
||
infinity. Otherwise the result is already rounded correctly
|
||
toward negative infinity. */
|
||
if (GET_MSR_RDAV (msr0))
|
||
++result;
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
SI
|
||
frvbf_media_average (SIM_CPU *current_cpu, SI reg1, SI reg2)
|
||
{
|
||
SI result;
|
||
result = do_media_average (current_cpu, reg1 & 0xffff, reg2 & 0xffff);
|
||
result &= 0xffff;
|
||
result |= do_media_average (current_cpu, (reg1 >> 16) & 0xffff,
|
||
(reg2 >> 16) & 0xffff) << 16;
|
||
return result;
|
||
}
|
||
|
||
/* Maintain a flag in order to know when to write the address of the next
|
||
VLIW instruction into the LR register. Used by JMPL. JMPIL, and CALL. */
|
||
void
|
||
frvbf_set_write_next_vliw_addr_to_LR (SIM_CPU *current_cpu, int value)
|
||
{
|
||
frvbf_write_next_vliw_addr_to_LR = value;
|
||
}
|
||
|
||
void
|
||
frvbf_set_ne_index (SIM_CPU *current_cpu, int index)
|
||
{
|
||
USI NE_flags[2];
|
||
|
||
/* Save the target register so interrupt processing can set its NE flag
|
||
in the event of an exception. */
|
||
frv_interrupt_state.ne_index = index;
|
||
|
||
/* Clear the NE flag of the target register. It will be reset if necessary
|
||
in the event of an exception. */
|
||
GET_NE_FLAGS (NE_flags, H_SPR_FNER0);
|
||
CLEAR_NE_FLAG (NE_flags, index);
|
||
SET_NE_FLAGS (H_SPR_FNER0, NE_flags);
|
||
}
|
||
|
||
void
|
||
frvbf_force_update (SIM_CPU *current_cpu)
|
||
{
|
||
CGEN_WRITE_QUEUE *q = CPU_WRITE_QUEUE (current_cpu);
|
||
int ix = CGEN_WRITE_QUEUE_INDEX (q);
|
||
if (ix > 0)
|
||
{
|
||
CGEN_WRITE_QUEUE_ELEMENT *item = CGEN_WRITE_QUEUE_ELEMENT (q, ix - 1);
|
||
item->flags |= FRV_WRITE_QUEUE_FORCE_WRITE;
|
||
}
|
||
}
|
||
|
||
/* Condition code logic. */
|
||
enum cr_ops {
|
||
andcr, orcr, xorcr, nandcr, norcr, andncr, orncr, nandncr, norncr,
|
||
num_cr_ops
|
||
};
|
||
|
||
enum cr_result {cr_undefined, cr_undefined1, cr_false, cr_true};
|
||
|
||
static enum cr_result
|
||
cr_logic[num_cr_ops][4][4] = {
|
||
/* andcr */
|
||
{
|
||
/* undefined undefined false true */
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined},
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined},
|
||
/* false */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined},
|
||
/* true */ {cr_undefined, cr_undefined, cr_false, cr_true }
|
||
},
|
||
/* orcr */
|
||
{
|
||
/* undefined undefined false true */
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_false, cr_true },
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_false, cr_true },
|
||
/* false */ {cr_false, cr_false, cr_false, cr_true },
|
||
/* true */ {cr_true, cr_true, cr_true, cr_true }
|
||
},
|
||
/* xorcr */
|
||
{
|
||
/* undefined undefined false true */
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined},
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined},
|
||
/* false */ {cr_undefined, cr_undefined, cr_false, cr_true },
|
||
/* true */ {cr_true, cr_true, cr_true, cr_false }
|
||
},
|
||
/* nandcr */
|
||
{
|
||
/* undefined undefined false true */
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined},
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined},
|
||
/* false */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined},
|
||
/* true */ {cr_undefined, cr_undefined, cr_true, cr_false }
|
||
},
|
||
/* norcr */
|
||
{
|
||
/* undefined undefined false true */
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_true, cr_false },
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_true, cr_false },
|
||
/* false */ {cr_true, cr_true, cr_true, cr_false },
|
||
/* true */ {cr_false, cr_false, cr_false, cr_false }
|
||
},
|
||
/* andncr */
|
||
{
|
||
/* undefined undefined false true */
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined},
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined},
|
||
/* false */ {cr_undefined, cr_undefined, cr_false, cr_true },
|
||
/* true */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined}
|
||
},
|
||
/* orncr */
|
||
{
|
||
/* undefined undefined false true */
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_false, cr_true },
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_false, cr_true },
|
||
/* false */ {cr_true, cr_true, cr_true, cr_true },
|
||
/* true */ {cr_false, cr_false, cr_false, cr_true }
|
||
},
|
||
/* nandncr */
|
||
{
|
||
/* undefined undefined false true */
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined},
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined},
|
||
/* false */ {cr_undefined, cr_undefined, cr_true, cr_false },
|
||
/* true */ {cr_undefined, cr_undefined, cr_undefined, cr_undefined}
|
||
},
|
||
/* norncr */
|
||
{
|
||
/* undefined undefined false true */
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_true, cr_false },
|
||
/* undefined */ {cr_undefined, cr_undefined, cr_true, cr_false },
|
||
/* false */ {cr_false, cr_false, cr_false, cr_false },
|
||
/* true */ {cr_true, cr_true, cr_true, cr_false }
|
||
}
|
||
};
|
||
|
||
UQI
|
||
frvbf_cr_logic (SIM_CPU *current_cpu, SI operation, UQI arg1, UQI arg2)
|
||
{
|
||
return cr_logic[operation][arg1][arg2];
|
||
}
|
||
|
||
/* Cache Manipulation. */
|
||
void
|
||
frvbf_insn_cache_preload (SIM_CPU *current_cpu, SI address, USI length, int lock)
|
||
{
|
||
/* If we need to count cycles, then the cache operation will be
|
||
initiated from the model profiling functions.
|
||
See frvbf_model_.... */
|
||
int hsr0 = GET_HSR0 ();
|
||
if (GET_HSR0_ICE (hsr0))
|
||
{
|
||
if (model_insn)
|
||
{
|
||
CPU_LOAD_ADDRESS (current_cpu) = address;
|
||
CPU_LOAD_LENGTH (current_cpu) = length;
|
||
CPU_LOAD_LOCK (current_cpu) = lock;
|
||
}
|
||
else
|
||
{
|
||
FRV_CACHE *cache = CPU_INSN_CACHE (current_cpu);
|
||
frv_cache_preload (cache, address, length, lock);
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
frvbf_data_cache_preload (SIM_CPU *current_cpu, SI address, USI length, int lock)
|
||
{
|
||
/* If we need to count cycles, then the cache operation will be
|
||
initiated from the model profiling functions.
|
||
See frvbf_model_.... */
|
||
int hsr0 = GET_HSR0 ();
|
||
if (GET_HSR0_DCE (hsr0))
|
||
{
|
||
if (model_insn)
|
||
{
|
||
CPU_LOAD_ADDRESS (current_cpu) = address;
|
||
CPU_LOAD_LENGTH (current_cpu) = length;
|
||
CPU_LOAD_LOCK (current_cpu) = lock;
|
||
}
|
||
else
|
||
{
|
||
FRV_CACHE *cache = CPU_DATA_CACHE (current_cpu);
|
||
frv_cache_preload (cache, address, length, lock);
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
frvbf_insn_cache_unlock (SIM_CPU *current_cpu, SI address)
|
||
{
|
||
/* If we need to count cycles, then the cache operation will be
|
||
initiated from the model profiling functions.
|
||
See frvbf_model_.... */
|
||
int hsr0 = GET_HSR0 ();
|
||
if (GET_HSR0_ICE (hsr0))
|
||
{
|
||
if (model_insn)
|
||
CPU_LOAD_ADDRESS (current_cpu) = address;
|
||
else
|
||
{
|
||
FRV_CACHE *cache = CPU_INSN_CACHE (current_cpu);
|
||
frv_cache_unlock (cache, address);
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
frvbf_data_cache_unlock (SIM_CPU *current_cpu, SI address)
|
||
{
|
||
/* If we need to count cycles, then the cache operation will be
|
||
initiated from the model profiling functions.
|
||
See frvbf_model_.... */
|
||
int hsr0 = GET_HSR0 ();
|
||
if (GET_HSR0_DCE (hsr0))
|
||
{
|
||
if (model_insn)
|
||
CPU_LOAD_ADDRESS (current_cpu) = address;
|
||
else
|
||
{
|
||
FRV_CACHE *cache = CPU_DATA_CACHE (current_cpu);
|
||
frv_cache_unlock (cache, address);
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
frvbf_insn_cache_invalidate (SIM_CPU *current_cpu, SI address, int all)
|
||
{
|
||
/* Make sure the insn was specified properly. -1 will be passed for ALL
|
||
for a icei with A=0. */
|
||
if (all == -1)
|
||
{
|
||
frv_queue_program_interrupt (current_cpu, FRV_ILLEGAL_INSTRUCTION);
|
||
return;
|
||
}
|
||
|
||
/* If we need to count cycles, then the cache operation will be
|
||
initiated from the model profiling functions.
|
||
See frvbf_model_.... */
|
||
if (model_insn)
|
||
{
|
||
/* Record the all-entries flag for use in profiling. */
|
||
FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (current_cpu);
|
||
ps->all_cache_entries = all;
|
||
CPU_LOAD_ADDRESS (current_cpu) = address;
|
||
}
|
||
else
|
||
{
|
||
FRV_CACHE *cache = CPU_INSN_CACHE (current_cpu);
|
||
if (all)
|
||
frv_cache_invalidate_all (cache, 0/* flush? */);
|
||
else
|
||
frv_cache_invalidate (cache, address, 0/* flush? */);
|
||
}
|
||
}
|
||
|
||
void
|
||
frvbf_data_cache_invalidate (SIM_CPU *current_cpu, SI address, int all)
|
||
{
|
||
/* Make sure the insn was specified properly. -1 will be passed for ALL
|
||
for a dcei with A=0. */
|
||
if (all == -1)
|
||
{
|
||
frv_queue_program_interrupt (current_cpu, FRV_ILLEGAL_INSTRUCTION);
|
||
return;
|
||
}
|
||
|
||
/* If we need to count cycles, then the cache operation will be
|
||
initiated from the model profiling functions.
|
||
See frvbf_model_.... */
|
||
if (model_insn)
|
||
{
|
||
/* Record the all-entries flag for use in profiling. */
|
||
FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (current_cpu);
|
||
ps->all_cache_entries = all;
|
||
CPU_LOAD_ADDRESS (current_cpu) = address;
|
||
}
|
||
else
|
||
{
|
||
FRV_CACHE *cache = CPU_DATA_CACHE (current_cpu);
|
||
if (all)
|
||
frv_cache_invalidate_all (cache, 0/* flush? */);
|
||
else
|
||
frv_cache_invalidate (cache, address, 0/* flush? */);
|
||
}
|
||
}
|
||
|
||
void
|
||
frvbf_data_cache_flush (SIM_CPU *current_cpu, SI address, int all)
|
||
{
|
||
/* Make sure the insn was specified properly. -1 will be passed for ALL
|
||
for a dcef with A=0. */
|
||
if (all == -1)
|
||
{
|
||
frv_queue_program_interrupt (current_cpu, FRV_ILLEGAL_INSTRUCTION);
|
||
return;
|
||
}
|
||
|
||
/* If we need to count cycles, then the cache operation will be
|
||
initiated from the model profiling functions.
|
||
See frvbf_model_.... */
|
||
if (model_insn)
|
||
{
|
||
/* Record the all-entries flag for use in profiling. */
|
||
FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (current_cpu);
|
||
ps->all_cache_entries = all;
|
||
CPU_LOAD_ADDRESS (current_cpu) = address;
|
||
}
|
||
else
|
||
{
|
||
FRV_CACHE *cache = CPU_DATA_CACHE (current_cpu);
|
||
if (all)
|
||
frv_cache_invalidate_all (cache, 1/* flush? */);
|
||
else
|
||
frv_cache_invalidate (cache, address, 1/* flush? */);
|
||
}
|
||
}
|