| /* 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? */); |
| } |
| } |