summaryrefslogtreecommitdiff
path: root/sim/mips/dv-tx3904cpu.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/mips/dv-tx3904cpu.c')
-rw-r--r--sim/mips/dv-tx3904cpu.c246
1 files changed, 246 insertions, 0 deletions
diff --git a/sim/mips/dv-tx3904cpu.c b/sim/mips/dv-tx3904cpu.c
new file mode 100644
index 00000000000..07b8521fc6f
--- /dev/null
+++ b/sim/mips/dv-tx3904cpu.c
@@ -0,0 +1,246 @@
+/* This file is part of the program GDB, the GNU debugger.
+
+ Copyright (C) 1998 Free Software Foundation, Inc.
+ Contributed by Cygnus Solutions.
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+
+#include "sim-main.h"
+#include "hw-main.h"
+
+/* DEVICE
+
+
+ tx3904cpu - tx3904 cpu virtual device
+
+
+ DESCRIPTION
+
+
+ Implements the external tx3904 functionality. This includes the
+ delivery of of interrupts generated from other devices and the
+ handling of device specific registers.
+
+
+ PROPERTIES
+
+ none
+
+
+ PORTS
+
+
+ reset (input)
+
+ Currently ignored.
+
+
+ nmi (input)
+
+ Deliver a non-maskable interrupt to the processor.
+
+
+ level (input)
+
+ Deliver a maskable interrupt of given level, corresponding to
+ IP[5:0], to processor.
+
+
+
+ BUGS
+
+
+ When delivering an interrupt, this code assumes that there is only
+ one processor (number 0).
+
+ This code does not attempt to be efficient at handling pending
+ interrupts. It simply schedules the interrupt delivery handler
+ every instruction cycle until all pending interrupts go away. An
+ alternative implementation might modify instructions that change
+ the PSW and have them check to see if the change makes an interrupt
+ delivery possible.
+
+ */
+
+
+
+struct tx3904cpu {
+ /* Pending interrupts for delivery by event handler */
+ int pending_reset, pending_nmi, pending_level;
+ struct hw_event* event;
+};
+
+
+
+/* input port ID's */
+
+enum {
+ RESET_PORT,
+ NMI_PORT,
+ LEVEL_PORT,
+};
+
+
+static const struct hw_port_descriptor tx3904cpu_ports[] = {
+
+ /* interrupt inputs */
+ { "reset", RESET_PORT, 0, input_port, },
+ { "nmi", NMI_PORT, 0, input_port, },
+ { "level", LEVEL_PORT, 0, input_port, },
+
+ { NULL, },
+};
+
+
+/* Finish off the partially created hw device. Attach our local
+ callbacks. Wire up our port names etc */
+
+static hw_port_event_method tx3904cpu_port_event;
+
+
+
+static void
+tx3904cpu_finish (struct hw *me)
+{
+ struct tx3904cpu *controller;
+
+ controller = HW_ZALLOC (me, struct tx3904cpu);
+ set_hw_data (me, controller);
+ set_hw_ports (me, tx3904cpu_ports);
+ set_hw_port_event (me, tx3904cpu_port_event);
+
+ /* Initialize the pending interrupt flags */
+ controller->pending_level = 0;
+ controller->pending_reset = 0;
+ controller->pending_nmi = 0;
+ controller->event = NULL;
+}
+
+
+
+/* An event arrives on an interrupt port */
+
+static void
+deliver_tx3904cpu_interrupt (struct hw *me,
+ void *data)
+{
+ struct tx3904cpu *controller = hw_data (me);
+ SIM_DESC sd = hw_system (me);
+ sim_cpu *cpu = STATE_CPU (sd, 0); /* NB: fix CPU 0. */
+ address_word cia = CIA_GET (cpu);
+
+#define CPU cpu
+#define SD current_state
+
+ if (controller->pending_reset)
+ {
+ controller->pending_reset = 0;
+ HW_TRACE ((me, "reset pc=0x%08lx", (long) CIA_GET (cpu)));
+ SignalExceptionNMIReset();
+ }
+ else if (controller->pending_nmi)
+ {
+ controller->pending_nmi = 0;
+ HW_TRACE ((me, "nmi pc=0x%08lx", (long) CIA_GET (cpu)));
+ SignalExceptionNMIReset();
+ }
+ else if (controller->pending_level)
+ {
+ HW_TRACE ((me, "interrupt level=%d pc=0x%08lx sr=0x%08lx",
+ controller->pending_level,
+ (long) CIA_GET (cpu), (long) SR));
+
+ /* Clear CAUSE register. It may stay this way if the interrupt
+ was cleared with a negative pending_level. */
+ CAUSE &= ~ (cause_IP_mask << cause_IP_shift);
+
+ if(controller->pending_level > 0) /* interrupt set */
+ {
+ /* set hardware-interrupt subfields of CAUSE register */
+ CAUSE |= (controller->pending_level & cause_IP_mask) << cause_IP_shift;
+
+ /* check for enabled / unmasked interrupts */
+ if((SR & status_IEc) &&
+ (controller->pending_level & ((SR >> status_IM_shift) & status_IM_mask)))
+ {
+ controller->pending_level = 0;
+ SignalExceptionInterrupt(0 /* dummy value */);
+ }
+ else
+ {
+ /* reschedule soon */
+ if(controller->event != NULL)
+ hw_event_queue_deschedule(me, controller->event);
+ controller->event =
+ hw_event_queue_schedule (me, 1, deliver_tx3904cpu_interrupt, NULL);
+ }
+ } /* interrupt set */
+ }
+#undef CPU cpu
+#undef SD current_state
+}
+
+
+static void
+tx3904cpu_port_event (struct hw *me,
+ int my_port,
+ struct hw *source,
+ int source_port,
+ int level)
+{
+ struct tx3904cpu *controller = hw_data (me);
+
+ switch (my_port)
+ {
+ case RESET_PORT:
+ controller->pending_reset = 1;
+ HW_TRACE ((me, "port-in reset"));
+ break;
+
+ case NMI_PORT:
+ controller->pending_nmi = 1;
+ HW_TRACE ((me, "port-in nmi"));
+ break;
+
+ case LEVEL_PORT:
+ /* level == 0 means that the interrupt was cleared */
+ if(level == 0)
+ controller->pending_level = -1; /* signal end of interrupt */
+ else
+ controller->pending_level = level;
+ HW_TRACE ((me, "port-in level=%d", level));
+ break;
+
+ default:
+ hw_abort (me, "bad switch");
+ break;
+ }
+
+ /* Schedule an event to be delivered immediately after current
+ instruction. */
+ if(controller->event != NULL)
+ hw_event_queue_deschedule(me, controller->event);
+ controller->event =
+ hw_event_queue_schedule (me, 0, deliver_tx3904cpu_interrupt, NULL);
+}
+
+
+const struct hw_descriptor dv_tx3904cpu_descriptor[] = {
+ { "tx3904cpu", tx3904cpu_finish, },
+ { NULL },
+};