1/*  This file is part of the program GDB, the GNU debugger.
2
3    Copyright (C) 1998, 2007 Free Software Foundation, Inc.
4    Contributed by Cygnus Solutions.
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19    */
20
21
22#include "sim-main.h"
23#include "hw-main.h"
24
25/* DEVICE
26
27
28   tx3904cpu - tx3904 cpu virtual device
29
30
31   DESCRIPTION
32
33
34   Implements the external tx3904 functionality.  This includes the
35   delivery of of interrupts generated from other devices and the
36   handling of device specific registers.
37
38
39   PROPERTIES
40
41   none
42
43
44   PORTS
45
46
47   reset (input)
48
49   Currently ignored.
50
51
52   nmi (input)
53
54   Deliver a non-maskable interrupt to the processor.
55
56
57   level (input)
58
59   Deliver a maskable interrupt of given level, corresponding to
60   IP[5:0], to processor.
61
62
63
64   BUGS
65
66
67   When delivering an interrupt, this code assumes that there is only
68   one processor (number 0).
69
70   This code does not attempt to be efficient at handling pending
71   interrupts.  It simply schedules the interrupt delivery handler
72   every instruction cycle until all pending interrupts go away.  An
73   alternative implementation might modify instructions that change
74   the PSW and have them check to see if the change makes an interrupt
75   delivery possible.
76
77   */
78
79
80
81struct tx3904cpu {
82  /* Pending interrupts for delivery by event handler */
83  int pending_reset, pending_nmi, pending_level;
84  struct hw_event* event;
85};
86
87
88
89/* input port ID's */
90
91enum {
92  RESET_PORT,
93  NMI_PORT,
94  LEVEL_PORT,
95};
96
97
98static const struct hw_port_descriptor tx3904cpu_ports[] = {
99
100  /* interrupt inputs */
101  { "reset", RESET_PORT, 0, input_port, },
102  { "nmi", NMI_PORT, 0, input_port, },
103  { "level", LEVEL_PORT, 0, input_port, },
104
105  { NULL, },
106};
107
108
109/* Finish off the partially created hw device.  Attach our local
110   callbacks.  Wire up our port names etc */
111
112static hw_port_event_method tx3904cpu_port_event;
113
114
115
116static void
117tx3904cpu_finish (struct hw *me)
118{
119  struct tx3904cpu *controller;
120
121  controller = HW_ZALLOC (me, struct tx3904cpu);
122  set_hw_data (me, controller);
123  set_hw_ports (me, tx3904cpu_ports);
124  set_hw_port_event (me, tx3904cpu_port_event);
125
126  /* Initialize the pending interrupt flags */
127  controller->pending_level = 0;
128  controller->pending_reset = 0;
129  controller->pending_nmi = 0;
130  controller->event = NULL;
131}
132
133
134
135/* An event arrives on an interrupt port */
136
137static void
138deliver_tx3904cpu_interrupt (struct hw *me,
139			    void *data)
140{
141  struct tx3904cpu *controller = hw_data (me);
142  SIM_DESC sd = hw_system (me);
143  sim_cpu *cpu = STATE_CPU (sd, 0); /* NB: fix CPU 0. */
144  address_word cia = CIA_GET (cpu);
145
146#define CPU cpu
147#define SD current_state
148
149  if (controller->pending_reset)
150    {
151      controller->pending_reset = 0;
152      HW_TRACE ((me, "reset pc=0x%08lx", (long) CIA_GET (cpu)));
153      SignalExceptionNMIReset();
154    }
155  else if (controller->pending_nmi)
156    {
157      controller->pending_nmi = 0;
158      HW_TRACE ((me, "nmi pc=0x%08lx", (long) CIA_GET (cpu)));
159      SignalExceptionNMIReset();
160    }
161  else if (controller->pending_level)
162    {
163      HW_TRACE ((me, "interrupt level=%d pc=0x%08lx sr=0x%08lx",
164		 controller->pending_level,
165		 (long) CIA_GET (cpu), (long) SR));
166
167      /* Clear CAUSE register.  It may stay this way if the interrupt
168	 was cleared with a negative pending_level. */
169      CAUSE &= ~ (cause_IP_mask << cause_IP_shift);
170
171      if(controller->pending_level > 0) /* interrupt set */
172	{
173	  /* set hardware-interrupt subfields of CAUSE register */
174	  CAUSE |= (controller->pending_level & cause_IP_mask) << cause_IP_shift;
175
176	  /* check for enabled / unmasked interrupts */
177	  if((SR & status_IEc) &&
178	     (controller->pending_level & ((SR >> status_IM_shift) & status_IM_mask)))
179	    {
180	      controller->pending_level = 0;
181	      SignalExceptionInterrupt(0 /* dummy value */);
182	    }
183	  else
184	    {
185	      /* reschedule soon */
186	      if(controller->event != NULL)
187		hw_event_queue_deschedule(me, controller->event);
188	      controller->event =
189		hw_event_queue_schedule (me, 1, deliver_tx3904cpu_interrupt, NULL);
190	    }
191	} /* interrupt set */
192    }
193#undef CPU cpu
194#undef SD current_state
195}
196
197
198static void
199tx3904cpu_port_event (struct hw *me,
200		     int my_port,
201		     struct hw *source,
202		     int source_port,
203		     int level)
204{
205  struct tx3904cpu *controller = hw_data (me);
206
207  switch (my_port)
208    {
209    case RESET_PORT:
210      controller->pending_reset = 1;
211      HW_TRACE ((me, "port-in reset"));
212      break;
213
214    case NMI_PORT:
215      controller->pending_nmi = 1;
216      HW_TRACE ((me, "port-in nmi"));
217      break;
218
219    case LEVEL_PORT:
220      /* level == 0 means that the interrupt was cleared */
221      if(level == 0)
222	controller->pending_level = -1; /* signal end of interrupt */
223      else
224	controller->pending_level = level;
225      HW_TRACE ((me, "port-in level=%d", level));
226      break;
227
228    default:
229      hw_abort (me, "bad switch");
230      break;
231    }
232
233  /* Schedule an event to be delivered immediately after current
234     instruction. */
235  if(controller->event != NULL)
236    hw_event_queue_deschedule(me, controller->event);
237  controller->event =
238    hw_event_queue_schedule (me, 0, deliver_tx3904cpu_interrupt, NULL);
239}
240
241
242const struct hw_descriptor dv_tx3904cpu_descriptor[] = {
243  { "tx3904cpu", tx3904cpu_finish, },
244  { NULL },
245};
246