aboutsummaryrefslogtreecommitdiff
path: root/sim/sparc/sparc32.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/sparc/sparc32.c')
-rw-r--r--sim/sparc/sparc32.c568
1 files changed, 568 insertions, 0 deletions
diff --git a/sim/sparc/sparc32.c b/sim/sparc/sparc32.c
new file mode 100644
index 0000000..6e7205a
--- /dev/null
+++ b/sim/sparc/sparc32.c
@@ -0,0 +1,568 @@
+/* sparc32 simulator support code
+ Copyright (C) 1999 Cygnus Solutions. */
+
+#define WANT_CPU sparc32
+#define WANT_CPU_SPARC32
+
+#include "sim-main.h"
+#include <signal.h>
+#include "libiberty.h"
+#include "bfd.h"
+#include "cgen-mem.h"
+#include "cgen-ops.h"
+#include "targ-vals.h"
+
+static void sparc32_init_regwins (SIM_CPU *current_cpu);
+static void sparc32_set_psr_no_cwp (SIM_CPU *current_cpu, USI newval);
+
+/* gdb register access support.
+ The contents of BUF are in target byte order. */
+
+int
+sparc32_fetch_register (SIM_CPU *current_cpu, int rn, unsigned char *buf, int len)
+{
+ if (rn < 32)
+ {
+ SETTSI (buf, a_sparc_h_gr_get (current_cpu, rn));
+ }
+ else
+ switch (rn)
+ {
+ case SPARC32_PC_REGNUM :
+ SETTSI (buf, a_sparc_h_pc_get (current_cpu));
+ break;
+ case SPARC32_NPC_REGNUM :
+ {
+ USI npc = a_sparc_h_npc_get (current_cpu);
+#if 0 /* experiment */
+ if (npc == NPC_NO_DELAY_INSN)
+ npc = a_sparc_h_pc_get (current_cpu) + 4;
+#endif
+ SETTSI (buf, npc);
+ break;
+ }
+ default :
+ return 0;
+ }
+
+ return -1; /*FIXME*/
+}
+
+/* gdb register access support.
+ The contents of BUF are in target byte order. */
+
+int
+sparc32_store_register (SIM_CPU *current_cpu, int rn, unsigned char *buf, int len)
+{
+ if (rn < 32)
+ {
+ a_sparc_h_gr_set (current_cpu, rn, GETTSI (buf));
+ }
+ else
+ switch (rn)
+ {
+ case SPARC32_PC_REGNUM :
+ a_sparc_h_pc_set (current_cpu, GETTSI (buf));
+ break;
+ case SPARC32_NPC_REGNUM :
+ a_sparc_h_npc_set (current_cpu, GETTSI (buf));
+ break;
+ default :
+ return 0;
+ }
+
+ return -1; /*FIXME*/
+}
+
+/* Initialization. */
+
+/* Initialize the program counter. */
+
+void
+sparc32_init_pc (SIM_CPU *current_cpu, SI pc, SI npc)
+{
+ SET_H_PC (pc);
+ SET_H_NPC (npc);
+}
+
+/* Do a pseudo power-on-reset.
+ USERLAND_P is non-zero to prepare to run a user-land program. */
+
+void
+sparc32_cold_reset (SIM_CPU *current_cpu, int userland_p)
+{
+ int i;
+
+ /* Initialize the PSR.
+ This has to be careful as we just want to initialize the CWP, we don't
+ want to "set" it (which causes the old window to be "swapped out").
+ ??? impl,ver need better values. */
+ sparc32_set_psr_no_cwp (current_cpu, 0 | PSR_S);
+
+ /* Initialize cwp directly (bypassing SET_H_CWP and SET_H_PSR) as we just
+ want to initialize things, not "swap" the current window out. */
+ CPU (h_cwp) = 0;
+ sparc32_init_regwins (current_cpu);
+
+ /* Mark the last window as invalid. This creates a distinguishable end
+ of register window stack. The last window is window 1 (mask 2) as
+ saves decrement CWP.
+ Note that the last and first window overlap. */
+ SET_H_WIM (2);
+
+ sparc32_init_pc (current_cpu, 0, 4);
+
+ for (i = 0; i < 32; ++i)
+ SET_H_GR (i, 0);
+ SET_H_FSR (0);
+}
+
+/* Do a warm reset (the reset trap). */
+
+void
+sparc32_warm_reset (SIM_CPU *current_cpu)
+{
+ /* FIXME: unimplemented yet */
+}
+
+/* Special purpose registers. */
+
+/* The PSR.
+ ??? add ability to specify a register as a set of bitfields. */
+
+USI
+sparc32_get_h_psr_handler (SIM_CPU *current_cpu)
+{
+ USI val;
+ val = CPU (h_psr) & (PSR_IMPL | PSR_VER);
+ val |= GET_H_ICC_C () ? PSR_C : 0;
+ val |= GET_H_ICC_N () ? PSR_N : 0;
+ val |= GET_H_ICC_V () ? PSR_V : 0;
+ val |= GET_H_ICC_Z () ? PSR_Z : 0;
+ val |= GET_H_EC () ? PSR_EC : 0;
+ val |= GET_H_EF () ? PSR_EF : 0;
+ val |= (GET_H_PIL () & 0xf) << 8;
+ val |= GET_H_S () ? PSR_S : 0;
+ val |= GET_H_PS () ? PSR_PS : 0;
+ val |= GET_H_ET () ? PSR_ET : 0;
+ val |= GET_H_CWP ();
+ return val;
+}
+
+/* Utility to set everything in the PSR except CWP
+ (needed by sparc32_cold_reset). */
+
+static void
+sparc32_set_psr_no_cwp (SIM_CPU *current_cpu, USI newval)
+{
+ SET_H_ICC_C ((newval & PSR_C) != 0);
+ SET_H_ICC_N ((newval & PSR_N) != 0);
+ SET_H_ICC_V ((newval & PSR_V) != 0);
+ SET_H_ICC_Z ((newval & PSR_Z) != 0);
+ SET_H_EC ((newval & PSR_EC) != 0);
+ SET_H_EF ((newval & PSR_EF) != 0);
+ SET_H_PIL ((newval & PSR_PIL) >> 8);
+ SET_H_S ((newval & PSR_S) != 0);
+ SET_H_PS ((newval & PSR_PS) != 0);
+ SET_H_ET ((newval & PSR_ET) != 0);
+}
+
+void
+sparc32_set_h_psr_handler (SIM_CPU *current_cpu, USI newval)
+{
+ sparc32_set_psr_no_cwp (current_cpu, newval);
+ SET_H_CWP (newval & PSR_CWP);
+}
+
+/* Register window support. */
+
+/* Allocate space for the register window mechanism.
+ This version doesn't have much to do.
+ Other register window implementations have more to do. */
+
+void
+sparc32_alloc_regwins (SIM_CPU *current_cpu, int nwindows)
+{
+ /* nothing to do in current window implementation */
+}
+
+void
+sparc32_free_regwins (SIM_CPU *current_cpu)
+{
+ /* nothing to do in current window implementation */
+}
+
+/* Initialize the register window control registers for running
+ user programs. */
+
+static void
+sparc32_init_regwins (SIM_CPU *current_cpu)
+{
+ /* nothing to do in current window implementation */
+}
+
+/* Assign a new value to CWP.
+ SET_H_CWP calls this.
+
+ This swaps out the current values of the o/l/i regs from `current_regs'
+ and swaps in the new values. */
+
+void
+sparc32_set_h_cwp_handler (SIM_CPU *current_cpu, int new)
+{
+ int old = GET_H_CWP ();
+
+ if (new < 0 || new >= GET_NWINDOWS ())
+ abort ();
+
+ CPU (h_cwp) = new;
+
+ /* Swap current values out of `current_regs'.
+ Do this even if old == new. */
+ sparc32_swapout_regwin (current_cpu, old);
+
+ /* Swap new values into `current_regs'. */
+ if (old != new)
+ sparc32_swapin_regwin (current_cpu, new);
+}
+
+/* Swap out the values in `current_regs'. */
+
+void
+sparc32_swapout_regwin (SIM_CPU *current_cpu, int win)
+{
+ int n = 8 * sizeof (SI);
+
+ memcpy (REAL_OREGS (current_cpu, win), CURRENT_OREGS (current_cpu), n);
+ memcpy (REAL_LREGS (current_cpu, win), CURRENT_LREGS (current_cpu), n);
+ memcpy (REAL_IREGS (current_cpu, win), CURRENT_IREGS (current_cpu), n);
+}
+
+/* Swap int values for `current_regs'. */
+
+void
+sparc32_swapin_regwin (SIM_CPU *current_cpu, int win)
+{
+ int n = 8 * sizeof (SI);
+
+ memcpy (CURRENT_OREGS (current_cpu), REAL_OREGS (current_cpu, win), n);
+ memcpy (CURRENT_LREGS (current_cpu), REAL_LREGS (current_cpu, win), n);
+ memcpy (CURRENT_IREGS (current_cpu), REAL_IREGS (current_cpu, win), n);
+}
+
+/* Create a new window. We assume there is room. */
+
+void
+sparc32_save_regwin (SIM_CPU *current_cpu)
+{
+ SET_H_CWP (NEXT_WIN (GET_H_CWP ()));
+}
+
+/* Pop a window. We assume no traps possible. */
+
+void
+sparc32_restore_regwin (SIM_CPU *current_cpu)
+{
+ SET_H_CWP (PREV_WIN (GET_H_CWP ()));
+}
+
+/* Flush the register windows to memory.
+ This is necessary, for example, when we want to walk the stack in gdb.
+ NO_ERRORS_P is non-zero if memory faults must be avoided. This is important
+ when returning to gdb, the processor has "stopped".
+
+ ??? At present we only handle user programs. */
+
+void
+sparc32_flush_regwins (SIM_CPU *current_cpu, IADDR pc, int no_errors_p)
+{
+ int i, win;
+ int count = GET_NWINDOWS ();
+
+ /* Flush the current window cache. */
+ sparc32_swapout_regwin (current_cpu, GET_H_CWP ());
+
+ /* For each register window that is marked valid, flush it to memory.
+ We start at the current window and move upwards on the stack. */
+
+ for (i = 0, win = GET_H_CWP (); i < count; i++, win = PREV_WIN (win))
+ {
+ /* Don't go passed an invalid window. */
+ if (! WINDOW_VALID_P (win, GET_H_WIM ()))
+ break;
+ sparc32_flush_regwin (current_cpu, pc, win, no_errors_p);
+ }
+}
+
+void
+sparc32_flush_regwin (SIM_CPU *cpu, IADDR pc, int win, int no_errors_p)
+{
+ int i;
+ USI sp,fp,addr;
+ /* Fetch pointers to lregs and iregs for this frame. */
+ SI *lregs = REAL_LREGS (cpu, win);
+ SI *iregs = REAL_IREGS (cpu, win);
+ SIM_DESC sd = CPU_STATE (cpu);
+
+ /* Fetch values of sp,fp for this frame. */
+ sp = REAL_OREGS (cpu, win) [6];
+ fp = REAL_IREGS (cpu, win) [6];
+
+ /* Exit early if there'd be a memory fault but we can't have any errors. */
+ if (no_errors_p)
+ {
+ /* Check if sp and fp indicate a proper save may not have been done. */
+ if (fp <= sp
+ || fp - sp < 8 * sizeof (SI))
+ return;
+ /* sp misaligned? */
+ if (sp & 3)
+ return;
+ }
+
+ /* Use sim_core_write_aligned_N here to handle endian conversions. */
+
+ addr = sp;
+ for (i = 0; i < 8; i++)
+ {
+ if (no_errors_p)
+ {
+ char reg[4];
+ SETTSI (reg, lregs[i]);
+ if (sim_core_write_buffer (sd, cpu, write_map, reg, addr, 4) == 0)
+ return;
+ }
+ else
+ sim_core_write_aligned_4 (cpu, pc, write_map, addr, lregs[i]);
+ addr += 4;
+ }
+ for (i = 0; i < 8; i++)
+ {
+ if (no_errors_p)
+ {
+ char reg[4];
+ SETTSI (reg, lregs[i]);
+ if (sim_core_write_buffer (sd, cpu, write_map, reg, addr, 4) == 0)
+ return;
+ }
+ else
+ sim_core_write_aligned_4 (cpu, pc, write_map, addr, iregs[i]);
+ addr += 4;
+ }
+}
+
+void
+sparc32_load_regwin (SIM_CPU *cpu, IADDR pc, int win)
+{
+ int i;
+ /* Fetch value of sp for this frame. */
+ SI addr = REAL_OREGS (cpu, win) [6];
+ /* Fetch pointers to lregs and iregs for this frame. */
+ SI *lregs = REAL_LREGS (cpu, win);
+ SI *iregs = REAL_IREGS (cpu, win);
+
+ /* Use sim_core_read_aligned_N here to handle endian conversions. */
+
+ for (i = 0; i < 8; i++)
+ {
+ lregs[i] = sim_core_read_aligned_4 (cpu, pc, read_map, addr);
+ addr += 4;
+ }
+ for (i = 0; i < 8; i++)
+ {
+ iregs[i] = sim_core_read_aligned_4 (cpu, pc, read_map, addr);
+ addr += 4;
+ }
+}
+
+/* Save/restore insns. */
+
+/* Handle the save instruction. */
+
+SI
+sparc32_do_save (SIM_CPU *current_cpu, IADDR pc, SI rs1, SI rs2_simm13)
+{
+ SI rd;
+ int oldwin,newwin,wim;
+
+ /* FIXME: Watch for stack overflow if user prog. */
+
+ /* Determine new window number and see if its bit is set in the
+ Window Invalid Mask. */
+ oldwin = GET_H_CWP ();
+ newwin = NEXT_WIN (oldwin);
+ wim = GET_H_WIM ();
+ if (! WINDOW_VALID_P (newwin, wim))
+ sparc32_window_overflow (current_cpu, pc);
+
+ /* `rs1' and `rs2_simm13' are based on the old window (which we want) */
+ rd = rs1 + rs2_simm13;
+
+ /* Switch to the new window. */
+ sparc32_save_regwin (current_cpu);
+
+ if (TRACE_INSN_P (current_cpu)) /* FIXME */
+ {
+ trace_result (current_cpu, "sp", 'x', GET_H_GR (H_GR__SP));
+ trace_result (current_cpu, "fp", 'x', GET_H_GR (H_GR__FP));
+ trace_result (current_cpu, "cwp", 'x', GET_H_CWP ());
+ }
+
+ /* `rd' will be saved in the new window by the semantic code. */
+ return rd;
+}
+
+/* Handle the restore instruction. */
+
+SI
+sparc32_do_restore (SIM_CPU *current_cpu, IADDR pc, SI rs1, SI rs2_simm13)
+{
+ SI rd;
+ int oldwin,newwin,wim;
+
+ /* Determine new window number and see if its bit is set in the
+ Window Invalid Mask. */
+ oldwin = GET_H_CWP ();
+ newwin = PREV_WIN (oldwin);
+ wim = GET_H_WIM ();
+ if (! WINDOW_VALID_P (newwin, wim))
+ sparc32_window_underflow (current_cpu, pc);
+
+ /* `rs1' and `rs2_simm13' are based on the old window (which we want) */
+ rd = rs1 + rs2_simm13;
+
+ /* Switch to the previous window. */
+ sparc32_restore_regwin (current_cpu);
+
+ if (TRACE_INSN_P (current_cpu)) /* FIXME */
+ {
+ trace_result (current_cpu, "sp", 'x', GET_H_GR (H_GR__SP));
+ trace_result (current_cpu, "fp", 'x', GET_H_GR (H_GR__FP));
+ trace_result (current_cpu, "cwp", 'x', GET_H_CWP ());
+ }
+
+ /* `rd' will be saved in the new window by the semantic code. */
+ return rd;
+}
+
+/* ASI accesses. */
+
+#define DEFINE_GETMEM(mode, size) \
+mode \
+XCONCAT3 (GETMEM,mode,ASI) (SIM_CPU *cpu, IADDR pc, ADDR a, INT asi) \
+{ \
+ return 0; /* FIXME:wip */ \
+}
+
+DEFINE_GETMEM (QI, 1)
+DEFINE_GETMEM (UQI, 1)
+DEFINE_GETMEM (HI, 2)
+DEFINE_GETMEM (UHI, 2)
+DEFINE_GETMEM (SI, 4)
+DEFINE_GETMEM (USI, 4)
+DEFINE_GETMEM (DI, 8)
+DEFINE_GETMEM (UDI, 8)
+
+#undef DEFINE_GETMEM
+
+#define DEFINE_SETMEM(mode, size) \
+void \
+XCONCAT3 (SETMEM,mode,ASI) (SIM_CPU *cpu, IADDR pc, ADDR a, INT asi, mode newval) \
+{ \
+ return; /* FIXME:wip */ \
+}
+
+DEFINE_SETMEM (QI, 1)
+DEFINE_SETMEM (UQI, 1)
+DEFINE_SETMEM (HI, 2)
+DEFINE_SETMEM (UHI, 2)
+DEFINE_SETMEM (SI, 4)
+DEFINE_SETMEM (USI, 4)
+DEFINE_SETMEM (DI, 8)
+DEFINE_SETMEM (UDI, 8)
+
+#undef SETMEM
+
+/* ldstub, swap insns */
+
+void
+sparc32_do_ldstub (SIM_CPU *current_cpu, IADDR pc, INT rd_regno,
+ SI rs1, SI rs2_simm13, INT asi)
+{
+}
+
+void
+sparc32_do_swap (SIM_CPU *current_cpu, IADDR pc, INT rd_regno,
+ SI rs1, SI rs2_simm13, INT asi)
+{
+}
+
+/* Profiling support. */
+
+#if WITH_PROFILE_MODEL_P
+
+/* FIXME: Some of these should be inline or macros. Later. */
+
+void
+sparc32_model_mark_get_h_gr (SIM_CPU *cpu, ARGBUF *abuf)
+{
+}
+
+void
+sparc32_model_mark_set_h_gr (SIM_CPU *cpu, ARGBUF *abuf)
+{
+}
+
+void
+sparc32_model_mark_busy_reg (SIM_CPU *cpu, ARGBUF *abuf)
+{
+}
+
+void
+sparc32_model_mark_unbusy_reg (SIM_CPU *cpu, ARGBUF *abuf)
+{
+}
+
+/* Initialize cycle counting for an insn.
+ FIRST_P is non-zero if this is the first insn in a set of parallel
+ insns. */
+
+void
+sparc32_model_insn_before (SIM_CPU *cpu, int first_p)
+{
+}
+
+/* Record the cycles computed for an insn.
+ LAST_P is non-zero if this is the last insn in a set of parallel insns,
+ and we update the total cycle count.
+ CYCLES is the cycle count of the insn. */
+
+void
+sparc32_model_insn_after (SIM_CPU *cpu, int last_p, int cycles)
+{
+}
+
+int
+sparc32_model_sparc32_def_u_exec (SIM_CPU *cpu, const IDESC *idesc,
+ int unit_num, int referenced)
+{
+ return idesc->timing->units[unit_num].done;
+}
+
+#endif /* WITH_PROFILE_MODEL_P */
+
+/* Debugging stuff. */
+
+/* Pretty print the control and integer registers.
+ This can be invoked with the user-defined "dump" command in gdb. */
+
+void
+sim_debug_dump ()
+{
+ extern SIM_DESC current_state;
+ host_callback *cb = STATE_CALLBACK (current_state);
+ SIM_CPU *current_cpu = STATE_CPU (current_state, 0);
+
+ sim_cb_printf (cb, "CPU Registers\n");
+ sim_cb_printf (cb, "CWP:%4d WIM:%4d\n", GET_H_CWP (), GET_H_WIM ());
+}