| /* CRIS v32 simulator support code |
| Copyright (C) 2004-2021 Free Software Foundation, Inc. |
| Contributed by Axis Communications. |
| |
| 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/>. */ |
| |
| /* The infrastructure is based on that of i960.c. */ |
| |
| /* This must come before any other includes. */ |
| #include "defs.h" |
| |
| #define WANT_CPU_CRISV32F |
| |
| #define SPECIFIC_U_EXEC_FN |
| #define SPECIFIC_U_SKIP4_FN |
| #define SPECIFIC_U_CONST16_FN |
| #define SPECIFIC_U_CONST32_FN |
| #define SPECIFIC_U_MEM_FN |
| #define SPECIFIC_U_MOVEM_FN |
| #define BASENUM 32 |
| #define CRIS_TLS_REGISTER 2 |
| #include "cris-tmpl.c" |
| |
| #if WITH_PROFILE_MODEL_P |
| |
| /* Re-use the bit position for the BZ register, since there are no stall |
| cycles for reading or writing it. */ |
| #define CRIS_BZ_REGNO 16 |
| #define CRIS_MODF_JUMP_MASK (1 << CRIS_BZ_REGNO) |
| /* Likewise for the WZ register, marking memory writes. */ |
| #define CRIS_WZ_REGNO 20 |
| #define CRIS_MODF_MEM_WRITE_MASK (1 << CRIS_WZ_REGNO) |
| #define CRIS_MOF_REGNO (16 + 7) |
| #define CRIS_ALWAYS_CONDITION 14 |
| |
| /* This macro must only be used in context where there's only one |
| dynamic cause for a penalty, except in the u-exec unit. */ |
| |
| #define PENALIZE1(CNT) \ |
| do \ |
| { \ |
| CPU_CRIS_MISC_PROFILE (current_cpu)->CNT++; \ |
| model_data->prev_prev_prev_modf_regs \ |
| = model_data->prev_prev_modf_regs; \ |
| model_data->prev_prev_modf_regs \ |
| = model_data->prev_modf_regs; \ |
| model_data->prev_modf_regs = 0; \ |
| model_data->prev_prev_prev_movem_dest_regs \ |
| = model_data->prev_prev_movem_dest_regs; \ |
| model_data->prev_prev_movem_dest_regs \ |
| = model_data->prev_movem_dest_regs; \ |
| model_data->prev_movem_dest_regs = 0; \ |
| } \ |
| while (0) |
| |
| |
| /* Model function for u-skip4 unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_skip4)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED) |
| { |
| /* Handle PC not being updated with pbb. FIXME: What if not pbb? */ |
| CPU (h_pc) += 4; |
| return 0; |
| } |
| |
| /* Model function for u-exec unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_exec)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED, |
| INT destreg_in, |
| INT srcreg, |
| INT destreg_out) |
| { |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| UINT modf_regs |
| = ((destreg_out == -1 ? 0 : (1 << destreg_out)) |
| | model_data->modf_regs); |
| |
| if (srcreg != -1) |
| { |
| if (model_data->prev_movem_dest_regs & (1 << srcreg)) |
| { |
| PENALIZE1 (movemdst_stall_count); |
| PENALIZE1 (movemdst_stall_count); |
| PENALIZE1 (movemdst_stall_count); |
| } |
| else if (model_data->prev_prev_movem_dest_regs & (1 << srcreg)) |
| { |
| PENALIZE1 (movemdst_stall_count); |
| PENALIZE1 (movemdst_stall_count); |
| } |
| else if (model_data->prev_prev_prev_movem_dest_regs & (1 << srcreg)) |
| PENALIZE1 (movemdst_stall_count); |
| } |
| |
| if (destreg_in != -1) |
| { |
| if (model_data->prev_movem_dest_regs & (1 << destreg_in)) |
| { |
| PENALIZE1 (movemdst_stall_count); |
| PENALIZE1 (movemdst_stall_count); |
| PENALIZE1 (movemdst_stall_count); |
| } |
| else if (model_data->prev_prev_movem_dest_regs & (1 << destreg_in)) |
| { |
| PENALIZE1 (movemdst_stall_count); |
| PENALIZE1 (movemdst_stall_count); |
| } |
| else if (model_data->prev_prev_prev_movem_dest_regs & (1 << destreg_in)) |
| PENALIZE1 (movemdst_stall_count); |
| } |
| |
| model_data->prev_prev_prev_modf_regs |
| = model_data->prev_prev_modf_regs; |
| model_data->prev_prev_modf_regs = model_data->prev_modf_regs; |
| model_data->prev_modf_regs = modf_regs; |
| model_data->modf_regs = 0; |
| |
| model_data->prev_prev_prev_movem_dest_regs |
| = model_data->prev_prev_movem_dest_regs; |
| model_data->prev_prev_movem_dest_regs = model_data->prev_movem_dest_regs; |
| model_data->prev_movem_dest_regs = model_data->movem_dest_regs; |
| model_data->movem_dest_regs = 0; |
| |
| /* Handle PC not being updated with pbb. FIXME: What if not pbb? */ |
| CPU (h_pc) += 2; |
| return 1; |
| } |
| |
| /* Special case used when the destination is a special register. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_exec_to_sr)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED, |
| INT srcreg, |
| INT specreg) |
| { |
| int specdest; |
| |
| if (specreg != -1) |
| specdest = specreg + 16; |
| else |
| abort (); |
| |
| return MY (XCONCAT3 (f_model_crisv,BASENUM,_u_exec)) |
| (current_cpu, NULL, 0, 0, -1, srcreg, |
| /* The positions for constant-zero registers BZ and WZ are recycled |
| for jump and memory-write markers. We must take precautions |
| here not to add false markers for them. It might be that the |
| hardware inserts stall cycles for instructions that actually try |
| and write those registers, but we'll burn that bridge when we |
| get to it; we'd have to find other free bits or make new |
| model_data variables. However, it's doubtful that there will |
| ever be a need to be cycle-correct for useless code, at least in |
| this particular simulator, mainly used for GCC testing. */ |
| specdest == CRIS_BZ_REGNO || specdest == CRIS_WZ_REGNO |
| ? -1 : specdest); |
| } |
| |
| |
| /* Special case for movem. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_exec_movem)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED, |
| INT srcreg, |
| INT destreg_out) |
| { |
| return MY (XCONCAT3 (f_model_crisv,BASENUM,_u_exec)) |
| (current_cpu, NULL, 0, 0, -1, srcreg, destreg_out); |
| } |
| |
| /* Model function for u-const16 unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_const16)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED) |
| { |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| |
| /* If the previous insn was a jump of some sort and this insn |
| straddles a cache-line, there's a one-cycle penalty. |
| FIXME: Test-cases for normal const16 and others, like branch. */ |
| if ((model_data->prev_modf_regs & CRIS_MODF_JUMP_MASK) |
| && (CPU (h_pc) & 0x1e) == 0x1e) |
| PENALIZE1 (jumptarget_stall_count); |
| |
| /* Handle PC not being updated with pbb. FIXME: What if not pbb? */ |
| CPU (h_pc) += 2; |
| |
| return 0; |
| } |
| |
| /* Model function for u-const32 unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_const32)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED) |
| { |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| |
| /* If the previous insn was a jump of some sort and this insn |
| straddles a cache-line, there's a one-cycle penalty. */ |
| if ((model_data->prev_modf_regs & CRIS_MODF_JUMP_MASK) |
| && (CPU (h_pc) & 0x1e) == 0x1c) |
| PENALIZE1 (jumptarget_stall_count); |
| |
| /* Handle PC not being updated with pbb. FIXME: What if not pbb? */ |
| CPU (h_pc) += 4; |
| |
| return 0; |
| } |
| |
| /* Model function for u-mem unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_mem)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED, |
| INT srcreg) |
| { |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| |
| if (srcreg == -1) |
| abort (); |
| |
| /* If srcreg references a register modified in the previous cycle |
| through other than autoincrement, then there's a penalty: one |
| cycle. */ |
| if (model_data->prev_modf_regs & (1 << srcreg)) |
| PENALIZE1 (memsrc_stall_count); |
| |
| return 0; |
| } |
| |
| /* Model function for u-mem-r unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_mem_r)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED) |
| { |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| |
| /* There's a two-cycle penalty for read after a memory write in any of |
| the two previous cycles, known as a cache read-after-write hazard. |
| |
| This model function (the model_data member access) depends on being |
| executed before the u-exec unit. */ |
| if ((model_data->prev_modf_regs & CRIS_MODF_MEM_WRITE_MASK) |
| || (model_data->prev_prev_modf_regs & CRIS_MODF_MEM_WRITE_MASK)) |
| { |
| PENALIZE1 (memraw_stall_count); |
| PENALIZE1 (memraw_stall_count); |
| } |
| |
| return 0; |
| } |
| |
| /* Model function for u-mem-w unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_mem_w)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED) |
| { |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| |
| /* Mark that memory has been written. This model function (the |
| model_data member access) depends on being executed after the |
| u-exec unit. */ |
| model_data->prev_modf_regs |= CRIS_MODF_MEM_WRITE_MASK; |
| |
| return 0; |
| } |
| |
| /* Model function for u-movem-rtom unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_movem_rtom)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED, |
| /* Deliberate order. */ |
| INT addrreg, INT limreg) |
| { |
| USI addr; |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| |
| if (limreg == -1 || addrreg == -1) |
| abort (); |
| |
| addr = GET_H_GR (addrreg); |
| |
| /* The movem-to-memory instruction must not move a register modified |
| in one of the previous two cycles. Enforce by adding penalty |
| cycles. */ |
| if (model_data->prev_modf_regs & ((1 << (limreg + 1)) - 1)) |
| { |
| PENALIZE1 (movemsrc_stall_count); |
| PENALIZE1 (movemsrc_stall_count); |
| } |
| else if (model_data->prev_prev_modf_regs & ((1 << (limreg + 1)) - 1)) |
| PENALIZE1 (movemsrc_stall_count); |
| |
| /* One-cycle penalty for each cache-line straddled. Use the |
| documented expressions. Unfortunately no penalty cycles are |
| eliminated by any penalty cycles above. We file these numbers |
| separately, since they aren't schedulable for all cases. */ |
| if ((addr >> 5) == (((addr + 4 * (limreg + 1)) - 1) >> 5)) |
| ; |
| else if ((addr >> 5) == (((addr + 4 * (limreg + 1)) - 1) >> 5) - 1) |
| PENALIZE1 (movemaddr_stall_count); |
| else if ((addr >> 5) == (((addr + 4 * (limreg + 1)) - 1) >> 5) - 2) |
| { |
| PENALIZE1 (movemaddr_stall_count); |
| PENALIZE1 (movemaddr_stall_count); |
| } |
| else |
| abort (); |
| |
| return 0; |
| } |
| |
| /* Model function for u-movem-mtor unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_movem_mtor)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED, |
| /* Deliberate order. */ |
| INT addrreg, INT limreg) |
| { |
| USI addr; |
| int nregs = limreg + 1; |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| |
| if (limreg == -1 || addrreg == -1) |
| abort (); |
| |
| addr = GET_H_GR (addrreg); |
| |
| /* One-cycle penalty for each cache-line straddled. Use the |
| documented expressions. One cycle is the norm; more cycles are |
| counted as penalties. Unfortunately no penalty cycles here |
| eliminate penalty cycles indicated in ->movem_dest_regs. */ |
| if ((addr >> 5) == (((addr + 4 * nregs) - 1) >> 5) - 1) |
| PENALIZE1 (movemaddr_stall_count); |
| else if ((addr >> 5) == (((addr + 4 * nregs) - 1) >> 5) - 2) |
| { |
| PENALIZE1 (movemaddr_stall_count); |
| PENALIZE1 (movemaddr_stall_count); |
| } |
| |
| model_data->modf_regs |= ((1 << nregs) - 1); |
| model_data->movem_dest_regs |= ((1 << nregs) - 1); |
| return 0; |
| } |
| |
| |
| /* Model function for u-branch unit. |
| FIXME: newpc and cc are always wrong. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM,_u_branch)) (SIM_CPU *current_cpu, |
| const IDESC *idesc, |
| int unit_num, int referenced) |
| { |
| CRIS_MISC_PROFILE *profp = CPU_CRIS_MISC_PROFILE (current_cpu); |
| USI pc = profp->old_pc; |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| int taken = profp->branch_taken; |
| int branch_index = (pc & (N_CRISV32_BRANCH_PREDICTORS - 1)) >> 1; |
| int pred_taken = (profp->branch_predictors[branch_index] & 2) != 0; |
| |
| if (taken != pred_taken) |
| { |
| PENALIZE1 (branch_stall_count); |
| PENALIZE1 (branch_stall_count); |
| } |
| |
| if (taken) |
| { |
| if (profp->branch_predictors[branch_index] < 3) |
| profp->branch_predictors[branch_index]++; |
| |
| return MY (XCONCAT3 (f_model_crisv,BASENUM,_u_jump)) |
| (current_cpu, idesc, unit_num, referenced, -1); |
| } |
| |
| if (profp->branch_predictors[branch_index] != 0) |
| profp->branch_predictors[branch_index]--; |
| |
| return 0; |
| } |
| |
| /* Model function for u-jump-r unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_jump_r)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED, |
| int regno) |
| { |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| |
| if (regno == -1) |
| abort (); |
| |
| /* For jump-to-register, the register must not have been modified the |
| last two cycles. Penalty: two cycles from the modifying insn. */ |
| if ((1 << regno) & model_data->prev_modf_regs) |
| { |
| PENALIZE1 (jumpsrc_stall_count); |
| PENALIZE1 (jumpsrc_stall_count); |
| } |
| else if ((1 << regno) & model_data->prev_prev_modf_regs) |
| PENALIZE1 (jumpsrc_stall_count); |
| |
| return 0; |
| } |
| |
| /* Model function for u-jump-sr unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM,_u_jump_sr)) (SIM_CPU *current_cpu, |
| const IDESC *idesc, |
| int unit_num, int referenced, |
| int sr_regno) |
| { |
| int regno; |
| |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| |
| if (sr_regno == -1) |
| abort (); |
| |
| regno = sr_regno + 16; |
| |
| /* For jump-to-register, the register must not have been modified the |
| last two cycles. Penalty: two cycles from the modifying insn. */ |
| if ((1 << regno) & model_data->prev_modf_regs) |
| { |
| PENALIZE1 (jumpsrc_stall_count); |
| PENALIZE1 (jumpsrc_stall_count); |
| } |
| else if ((1 << regno) & model_data->prev_prev_modf_regs) |
| PENALIZE1 (jumpsrc_stall_count); |
| |
| return |
| MY (XCONCAT3 (f_model_crisv,BASENUM,_u_jump)) (current_cpu, idesc, |
| unit_num, referenced, -1); |
| } |
| |
| /* Model function for u-jump unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_jump)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED, |
| int out_sr_regno) |
| { |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| |
| /* Mark that we made a jump. */ |
| model_data->modf_regs |
| |= (CRIS_MODF_JUMP_MASK |
| | (out_sr_regno == -1 || out_sr_regno == CRIS_BZ_REGNO |
| ? 0 : (1 << (out_sr_regno + 16)))); |
| return 0; |
| } |
| |
| /* Model function for u-multiply unit. */ |
| |
| int |
| MY (XCONCAT3 (f_model_crisv,BASENUM, |
| _u_multiply)) (SIM_CPU *current_cpu, |
| const IDESC *idesc ATTRIBUTE_UNUSED, |
| int unit_num ATTRIBUTE_UNUSED, |
| int referenced ATTRIBUTE_UNUSED, |
| int srcreg, int destreg) |
| { |
| MODEL_CRISV32_DATA *model_data |
| = (MODEL_CRISV32_DATA *) CPU_MODEL_DATA (current_cpu); |
| |
| /* Sanity-check for cases that should never happen. */ |
| if (srcreg == -1 || destreg == -1) |
| abort (); |
| |
| /* This takes extra cycles when one of the inputs has been modified |
| through other than autoincrement in the previous cycle. Penalty: |
| one cycle. */ |
| if (((1 << srcreg) | (1 << destreg)) & model_data->prev_modf_regs) |
| PENALIZE1 (mulsrc_stall_count); |
| |
| /* We modified the multiplication destination (marked in u-exec) and |
| the MOF register. */ |
| model_data->modf_regs |= (1 << CRIS_MOF_REGNO); |
| return 0; |
| } |
| |
| #endif /* WITH_PROFILE_MODEL_P */ |
| |
| int |
| MY (deliver_interrupt) (SIM_CPU *current_cpu, |
| enum cris_interrupt_type type, |
| unsigned int vec) |
| { |
| unsigned32 old_ccs, shifted_ccs, new_ccs; |
| unsigned char entryaddr_le[4]; |
| int was_user; |
| SIM_DESC sd = CPU_STATE (current_cpu); |
| unsigned32 entryaddr; |
| |
| /* We haven't implemented other interrupt-types yet. */ |
| if (type != CRIS_INT_INT) |
| abort (); |
| |
| /* We're called outside of branch delay slots etc, so we don't check |
| for that. */ |
| if (!GET_H_IBIT_V32 ()) |
| return 0; |
| |
| old_ccs = GET_H_SR_V32 (H_SR_CCS); |
| shifted_ccs = (old_ccs << 10) & ((1 << 30) - 1); |
| |
| /* The M bit is handled by code below and the M bit setter function, but |
| we need to preserve the Q bit. */ |
| new_ccs = shifted_ccs | (old_ccs & (unsigned32) 0x80000000UL); |
| was_user = GET_H_UBIT_V32 (); |
| |
| /* We need to force kernel mode since the setter method doesn't allow |
| it. Then we can use setter methods at will, since they then |
| recognize that we're in kernel mode. */ |
| CPU (h_ubit_v32) = 0; |
| |
| SET_H_SR (H_SR_CCS, new_ccs); |
| |
| if (was_user) |
| { |
| /* These methods require that user mode is unset. */ |
| SET_H_SR (H_SR_USP, GET_H_GR (H_GR_SP)); |
| SET_H_GR (H_GR_SP, GET_H_KERNEL_SP ()); |
| } |
| |
| /* ERP setting is simplified by not taking interrupts in delay-slots |
| or when halting. */ |
| /* For all other exceptions than guru and NMI, store the return |
| address in ERP and set EXS and EXD here. */ |
| SET_H_SR (H_SR_ERP, GET_H_PC ()); |
| |
| /* Simplified by not having exception types (fault indications). */ |
| SET_H_SR_V32 (H_SR_EXS, (vec * 256)); |
| SET_H_SR_V32 (H_SR_EDA, 0); |
| |
| if (sim_core_read_buffer (sd, |
| current_cpu, |
| read_map, entryaddr_le, |
| GET_H_SR (H_SR_EBP) + vec * 4, 4) == 0) |
| { |
| /* Nothing to do actually; either abort or send a signal. */ |
| sim_core_signal (sd, current_cpu, CPU_PC_GET (current_cpu), 0, 4, |
| GET_H_SR (H_SR_EBP) + vec * 4, |
| read_transfer, sim_core_unmapped_signal); |
| return 0; |
| } |
| |
| entryaddr = bfd_getl32 (entryaddr_le); |
| SET_H_PC (entryaddr); |
| |
| return 1; |
| } |