]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/d30v/engine.c
Initial creation of sourceware repository
[thirdparty/binutils-gdb.git] / sim / d30v / engine.c
1 /* This file is part of the program psim.
2
3 Copyright (C) 1994-1997, Andrew Cagney <cagney@highland.com.au>
4 Copyright (C) 1996, 1997, Free Software Foundation
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 2 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, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 */
21
22
23 #ifndef ENGINE_C
24 #define ENGINE_C
25
26 #include "sim-main.h"
27
28 #include <stdio.h>
29 #include <ctype.h>
30
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #else
38 #ifdef HAVE_STRINGS_H
39 #include <strings.h>
40 #endif
41 #endif
42
43 static void
44 do_stack_swap (SIM_DESC sd)
45 {
46 sim_cpu *cpu = STATE_CPU (sd, 0);
47 unsigned new_sp = (PSW_VAL(PSW_SM) != 0);
48 if (cpu->regs.current_sp != new_sp)
49 {
50 cpu->regs.sp[cpu->regs.current_sp] = SP;
51 cpu->regs.current_sp = new_sp;
52 SP = cpu->regs.sp[cpu->regs.current_sp];
53 }
54 }
55
56 #if WITH_TRACE
57 /* Implement ALU tracing of 32-bit registers. */
58 static void
59 trace_alu32 (SIM_DESC sd,
60 sim_cpu *cpu,
61 address_word cia,
62 unsigned32 *ptr)
63 {
64 unsigned32 value = *ptr;
65
66 if (ptr >= &GPR[0] && ptr <= &GPR[NR_GENERAL_PURPOSE_REGISTERS])
67 trace_one_insn (sd, cpu, cia, 1, "engine.c", __LINE__, "alu",
68 "Set register r%-2d = 0x%.8lx (%ld)",
69 ptr - &GPR[0], (long)value, (long)value);
70
71 else if (ptr == &PSW || ptr == &bPSW || ptr == &DPSW)
72 trace_one_insn (sd, cpu, cia, 1, "engine.c", __LINE__, "alu",
73 "Set register %s = 0x%.8lx%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
74 (ptr == &PSW) ? "psw" : ((ptr == &bPSW) ? "bpsw" : "dpsw"),
75 (long)value,
76 (value & (0x80000000 >> PSW_SM)) ? ", sm" : "",
77 (value & (0x80000000 >> PSW_EA)) ? ", ea" : "",
78 (value & (0x80000000 >> PSW_DB)) ? ", db" : "",
79 (value & (0x80000000 >> PSW_DS)) ? ", ds" : "",
80 (value & (0x80000000 >> PSW_IE)) ? ", ie" : "",
81 (value & (0x80000000 >> PSW_RP)) ? ", rp" : "",
82 (value & (0x80000000 >> PSW_MD)) ? ", md" : "",
83 (value & (0x80000000 >> PSW_F0)) ? ", f0" : "",
84 (value & (0x80000000 >> PSW_F1)) ? ", f1" : "",
85 (value & (0x80000000 >> PSW_F2)) ? ", f2" : "",
86 (value & (0x80000000 >> PSW_F3)) ? ", f3" : "",
87 (value & (0x80000000 >> PSW_S)) ? ", s" : "",
88 (value & (0x80000000 >> PSW_V)) ? ", v" : "",
89 (value & (0x80000000 >> PSW_VA)) ? ", va" : "",
90 (value & (0x80000000 >> PSW_C)) ? ", c" : "");
91
92 else if (ptr >= &CREG[0] && ptr <= &CREG[NR_CONTROL_REGISTERS])
93 trace_one_insn (sd, cpu, cia, 1, "engine.c", __LINE__, "alu",
94 "Set register cr%d = 0x%.8lx (%ld)",
95 ptr - &CREG[0], (long)value, (long)value);
96 }
97
98 /* Implement ALU tracing of 32-bit registers. */
99 static void
100 trace_alu64 (SIM_DESC sd,
101 sim_cpu *cpu,
102 address_word cia,
103 unsigned64 *ptr)
104 {
105 unsigned64 value = *ptr;
106
107 if (ptr >= &ACC[0] && ptr <= &ACC[NR_ACCUMULATORS])
108 trace_one_insn (sd, cpu, cia, 1, "engine.c", __LINE__, "alu",
109 "Set register a%-2d = 0x%.8lx 0x%.8lx",
110 ptr - &ACC[0],
111 (unsigned long)(unsigned32)(value >> 32),
112 (unsigned long)(unsigned32)value);
113
114 }
115 #endif
116
117 /* Process all of the queued up writes in order now */
118 void
119 unqueue_writes (SIM_DESC sd,
120 sim_cpu *cpu,
121 address_word cia)
122 {
123 int i, num;
124 int did_psw = 0;
125 unsigned32 *psw_addr = &PSW;
126
127 num = WRITE32_NUM;
128 for (i = 0; i < num; i++)
129 {
130 unsigned32 mask = WRITE32_MASK (i);
131 unsigned32 *ptr = WRITE32_PTR (i);
132 unsigned32 value = (*ptr & ~mask) | (WRITE32_VALUE (i) & mask);
133 int j;
134
135 if (ptr == psw_addr)
136 {
137 /* If MU instruction was not a MVTSYS (lkr), resolve PSW
138 contention in favour of IU. */
139 if(! STATE_CPU (sd, 0)->left_kills_right_p)
140 {
141 /* Detect contention in parallel writes to the same PSW flags.
142 The hardware allows the updates from IU to prevail over
143 those from MU. */
144
145 unsigned32 flag_bits =
146 BIT32 (PSW_F0) | BIT32 (PSW_F1) |
147 BIT32 (PSW_F2) | BIT32 (PSW_F3) |
148 BIT32 (PSW_S) | BIT32 (PSW_V) |
149 BIT32 (PSW_VA) | BIT32 (PSW_C);
150 unsigned32 my_flag_bits = mask & flag_bits;
151
152 for (j = i + 1; j < num; j++)
153 if (WRITE32_PTR (j) == psw_addr && /* write to PSW */
154 WRITE32_MASK (j) & my_flag_bits) /* some of the same flags */
155 {
156 /* Recompute local mask & value, to suppress this
157 earlier write to the same flag bits. */
158
159 unsigned32 new_mask = mask & ~(WRITE32_MASK (j) & my_flag_bits);
160
161 /* There is a special case for the VA (accumulated
162 overflow) flag, in that it is only included in the
163 second instruction's mask if the overflow
164 occurred. Yet the hardware still suppresses the
165 first instruction's update to VA. So we kludge
166 this by inferring PSW_V -> PSW_VA for the second
167 instruction. */
168
169 if (WRITE32_MASK (j) & BIT32 (PSW_V))
170 {
171 new_mask &= ~BIT32 (PSW_VA);
172 }
173
174 value = (*ptr & ~new_mask) | (WRITE32_VALUE (i) & new_mask);
175 }
176 }
177
178 did_psw = 1;
179 }
180
181 *ptr = value;
182
183 #if WITH_TRACE
184 if (TRACE_ALU_P (cpu))
185 trace_alu32 (sd, cpu, cia, ptr);
186 #endif
187 }
188
189 num = WRITE64_NUM;
190 for (i = 0; i < num; i++)
191 {
192 unsigned64 *ptr = WRITE64_PTR (i);
193 *ptr = WRITE64_VALUE (i);
194
195 #if WITH_TRACE
196 if (TRACE_ALU_P (cpu))
197 trace_alu64 (sd, cpu, cia, ptr);
198 #endif
199 }
200
201 WRITE32_NUM = 0;
202 WRITE64_NUM = 0;
203
204 if (DID_TRAP == 1) /* ordinary trap */
205 {
206 bPSW = PSW;
207 PSW &= (BIT32 (PSW_DB) | BIT32 (PSW_SM));
208 did_psw = 1;
209 }
210 else if (DID_TRAP == 2) /* debug trap */
211 {
212 DPSW = PSW;
213 PSW &= BIT32 (PSW_DS);
214 PSW |= BIT32 (PSW_DS);
215 did_psw = 1;
216 }
217 DID_TRAP = 0;
218
219 if (did_psw)
220 do_stack_swap (sd);
221 }
222
223
224 /* SIMULATE INSTRUCTIONS, various different ways of achieving the same
225 thing */
226
227 static address_word
228 do_long (SIM_DESC sd,
229 l_instruction_word instruction,
230 address_word cia)
231 {
232 address_word nia = l_idecode_issue(sd,
233 instruction,
234 cia);
235
236 unqueue_writes (sd, STATE_CPU (sd, 0), cia);
237 return nia;
238 }
239
240 static address_word
241 do_2_short (SIM_DESC sd,
242 s_instruction_word insn1,
243 s_instruction_word insn2,
244 cpu_units unit,
245 address_word cia)
246 {
247 address_word nia;
248
249 /* run the first instruction */
250 STATE_CPU (sd, 0)->unit = unit;
251 STATE_CPU (sd, 0)->left_kills_right_p = 0;
252 nia = s_idecode_issue(sd,
253 insn1,
254 cia);
255
256 unqueue_writes (sd, STATE_CPU (sd, 0), cia);
257
258 /* Only do the second instruction if the PC has not changed */
259 if ((nia == INVALID_INSTRUCTION_ADDRESS) &&
260 (! STATE_CPU (sd, 0)->left_kills_right_p)) {
261 STATE_CPU (sd, 0)->unit = any_unit;
262 nia = s_idecode_issue (sd,
263 insn2,
264 cia);
265
266 unqueue_writes (sd, STATE_CPU (sd, 0), cia);
267 }
268
269 STATE_CPU (sd, 0)->left_kills_right_p = 0;
270 return nia;
271 }
272
273 static address_word
274 do_parallel (SIM_DESC sd,
275 s_instruction_word left_insn,
276 s_instruction_word right_insn,
277 address_word cia)
278 {
279 address_word nia_left;
280 address_word nia_right;
281 address_word nia;
282
283 /* run the first instruction */
284 STATE_CPU (sd, 0)->unit = memory_unit;
285 STATE_CPU (sd, 0)->left_kills_right_p = 0;
286 nia_left = s_idecode_issue(sd,
287 left_insn,
288 cia);
289
290 /* run the second instruction */
291 STATE_CPU (sd, 0)->unit = integer_unit;
292 nia_right = s_idecode_issue(sd,
293 right_insn,
294 cia);
295
296 /* merge the PC's */
297 if (nia_left == INVALID_INSTRUCTION_ADDRESS) {
298 if (nia_right == INVALID_INSTRUCTION_ADDRESS)
299 nia = INVALID_INSTRUCTION_ADDRESS;
300 else
301 nia = nia_right;
302 }
303 else {
304 if (nia_right == INVALID_INSTRUCTION_ADDRESS)
305 nia = nia_left;
306 else {
307 sim_engine_abort (sd, STATE_CPU (sd, 0), cia, "parallel jumps");
308 nia = INVALID_INSTRUCTION_ADDRESS;
309 }
310 }
311
312 unqueue_writes (sd, STATE_CPU (sd, 0), cia);
313 return nia;
314 }
315
316
317 typedef enum {
318 p_insn = 0,
319 long_insn = 3,
320 l_r_insn = 1,
321 r_l_insn = 2,
322 } instruction_types;
323
324 STATIC_INLINE instruction_types
325 instruction_type(l_instruction_word insn)
326 {
327 int fm0 = MASKED64(insn, 0, 0) != 0;
328 int fm1 = MASKED64(insn, 32, 32) != 0;
329 return ((fm0 << 1) | fm1);
330 }
331
332
333
334 void
335 sim_engine_run (SIM_DESC sd,
336 int last_cpu_nr,
337 int nr_cpus,
338 int siggnal)
339 {
340 while (1)
341 {
342 address_word cia = PC;
343 address_word nia;
344 l_instruction_word insn = IMEM(cia);
345 int rp_was_set;
346 int rpt_c_was_nonzero;
347
348 /* Before executing the instruction, we need to test whether or
349 not RPT_C is greater than zero, and save that state for use
350 after executing the instruction. In particular, we need to
351 not care whether the instruction changes RPT_C itself. */
352
353 rpt_c_was_nonzero = (RPT_C > 0);
354
355 /* Before executing the instruction, we need to check to see if
356 we have to decrement RPT_C, the repeat count register. Do this
357 if PC == RPT_E, but only if we are in an active repeat block. */
358
359 if (PC == RPT_E &&
360 (RPT_C > 0 || PSW_VAL (PSW_RP) != 0))
361 {
362 RPT_C --;
363 }
364
365 /* Now execute the instruction at PC */
366
367 switch (instruction_type (insn))
368 {
369 case long_insn:
370 nia = do_long (sd, insn, cia);
371 break;
372 case r_l_insn:
373 /* L <- R */
374 nia = do_2_short (sd, insn, insn >> 32, integer_unit, cia);
375 break;
376 case l_r_insn:
377 /* L -> R */
378 nia = do_2_short (sd, insn >> 32, insn, memory_unit, cia);
379 break;
380 case p_insn:
381 nia = do_parallel (sd, insn >> 32, insn, cia);
382 break;
383 default:
384 sim_engine_abort (sd, STATE_CPU (sd, 0), cia,
385 "internal error - engine_run_until_stop - bad switch");
386 nia = -1;
387 }
388
389 if (TRACE_ACTION)
390 {
391 if (TRACE_ACTION & TRACE_ACTION_CALL)
392 call_occurred (sd, STATE_CPU (sd, 0), cia, nia);
393
394 if (TRACE_ACTION & TRACE_ACTION_RETURN)
395 return_occurred (sd, STATE_CPU (sd, 0), cia, nia);
396
397 TRACE_ACTION = 0;
398 }
399
400 /* Check now to see if we need to reset the RP bit in the PSW.
401 There are three conditions for this, the RP bit is already
402 set (just a speed optimization), the instruction we just
403 executed is the last instruction in the loop, and the repeat
404 count is currently zero. */
405
406 rp_was_set = PSW_VAL (PSW_RP);
407 if (rp_was_set && (PC == RPT_E) && RPT_C == 0)
408 {
409 PSW_SET (PSW_RP, 0);
410 }
411
412 /* Now update the PC. If we just executed a jump instruction,
413 that takes precedence over everything else. Next comes
414 branching back to RPT_S as a result of a loop. Finally, the
415 default is to simply advance to the next inline
416 instruction. */
417
418 if (nia != INVALID_INSTRUCTION_ADDRESS)
419 {
420 PC = nia;
421 }
422 else if (rp_was_set && rpt_c_was_nonzero && (PC == RPT_E))
423 {
424 PC = RPT_S;
425 }
426 else
427 {
428 PC = cia + 8;
429 }
430
431 /* Check for DDBT (debugger debug trap) condition. Do this after
432 the repeat block checks so the excursion to the trap handler does
433 not alter looping state. */
434
435 if (cia == IBA && PSW_VAL (PSW_DB))
436 {
437 DPC = PC;
438 PSW_SET (PSW_EA, 1);
439 DPSW = PSW;
440 /* clear all bits in PSW except SM */
441 PSW &= BIT32 (PSW_SM);
442 /* add DS bit */
443 PSW |= BIT32 (PSW_DS);
444 /* dispatch to DDBT handler */
445 PC = 0xfffff128; /* debugger_debug_trap_address */
446 }
447
448 /* process any events */
449 /* FIXME - should L->R or L<-R insns count as two cycles? */
450 if (sim_events_tick (sd))
451 {
452 sim_events_process (sd);
453 }
454 }
455 }
456
457
458 /* d30v external interrupt handler.
459
460 Note: This should be replaced by a proper interrupt delivery
461 mechanism. This interrupt mechanism discards later interrupts if
462 an earlier interrupt hasn't been delivered.
463
464 Note: This interrupt mechanism does not reset its self when the
465 simulator is re-opened. */
466
467 void
468 d30v_interrupt_event (SIM_DESC sd,
469 void *data)
470 {
471 if (PSW_VAL (PSW_IE))
472 /* interrupts not masked */
473 {
474 /* scrub any pending interrupt */
475 if (sd->pending_interrupt != NULL)
476 sim_events_deschedule (sd, sd->pending_interrupt);
477 /* deliver */
478 bPSW = PSW;
479 bPC = PC;
480 PSW = 0;
481 PC = 0xfffff138; /* external interrupt */
482 do_stack_swap (sd);
483 }
484 else if (sd->pending_interrupt == NULL)
485 /* interrupts masked and no interrupt pending */
486 {
487 sd->pending_interrupt = sim_events_schedule (sd, 1,
488 d30v_interrupt_event,
489 data);
490 }
491 }
492
493 #endif