Commit | Line | Data |
---|---|---|
c906108c SS |
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 | { | |
7a292a7a | 137 | /* If MU instruction was not a MVTSYS, resolve PSW |
c906108c | 138 | contention in favour of IU. */ |
7a292a7a | 139 | if(! STATE_CPU (sd, 0)->mvtsys_left_p) |
c906108c SS |
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; | |
7a292a7a | 252 | STATE_CPU (sd, 0)->mvtsys_left_p = 0; |
c906108c SS |
253 | nia = s_idecode_issue(sd, |
254 | insn1, | |
255 | cia); | |
256 | ||
257 | unqueue_writes (sd, STATE_CPU (sd, 0), cia); | |
258 | ||
259 | /* Only do the second instruction if the PC has not changed */ | |
260 | if ((nia == INVALID_INSTRUCTION_ADDRESS) && | |
261 | (! STATE_CPU (sd, 0)->left_kills_right_p)) { | |
262 | STATE_CPU (sd, 0)->unit = any_unit; | |
263 | nia = s_idecode_issue (sd, | |
264 | insn2, | |
265 | cia); | |
266 | ||
267 | unqueue_writes (sd, STATE_CPU (sd, 0), cia); | |
268 | } | |
269 | ||
270 | STATE_CPU (sd, 0)->left_kills_right_p = 0; | |
7a292a7a | 271 | STATE_CPU (sd, 0)->mvtsys_left_p = 0; |
c906108c SS |
272 | return nia; |
273 | } | |
274 | ||
275 | static address_word | |
276 | do_parallel (SIM_DESC sd, | |
277 | s_instruction_word left_insn, | |
278 | s_instruction_word right_insn, | |
279 | address_word cia) | |
280 | { | |
281 | address_word nia_left; | |
282 | address_word nia_right; | |
283 | address_word nia; | |
284 | ||
285 | /* run the first instruction */ | |
286 | STATE_CPU (sd, 0)->unit = memory_unit; | |
287 | STATE_CPU (sd, 0)->left_kills_right_p = 0; | |
7a292a7a | 288 | STATE_CPU (sd, 0)->mvtsys_left_p = 0; |
c906108c SS |
289 | nia_left = s_idecode_issue(sd, |
290 | left_insn, | |
291 | cia); | |
292 | ||
293 | /* run the second instruction */ | |
294 | STATE_CPU (sd, 0)->unit = integer_unit; | |
295 | nia_right = s_idecode_issue(sd, | |
296 | right_insn, | |
297 | cia); | |
298 | ||
299 | /* merge the PC's */ | |
300 | if (nia_left == INVALID_INSTRUCTION_ADDRESS) { | |
301 | if (nia_right == INVALID_INSTRUCTION_ADDRESS) | |
302 | nia = INVALID_INSTRUCTION_ADDRESS; | |
303 | else | |
304 | nia = nia_right; | |
305 | } | |
306 | else { | |
307 | if (nia_right == INVALID_INSTRUCTION_ADDRESS) | |
308 | nia = nia_left; | |
309 | else { | |
310 | sim_engine_abort (sd, STATE_CPU (sd, 0), cia, "parallel jumps"); | |
311 | nia = INVALID_INSTRUCTION_ADDRESS; | |
312 | } | |
313 | } | |
314 | ||
315 | unqueue_writes (sd, STATE_CPU (sd, 0), cia); | |
316 | return nia; | |
317 | } | |
318 | ||
319 | ||
320 | typedef enum { | |
321 | p_insn = 0, | |
322 | long_insn = 3, | |
323 | l_r_insn = 1, | |
324 | r_l_insn = 2, | |
325 | } instruction_types; | |
326 | ||
327 | STATIC_INLINE instruction_types | |
328 | instruction_type(l_instruction_word insn) | |
329 | { | |
330 | int fm0 = MASKED64(insn, 0, 0) != 0; | |
331 | int fm1 = MASKED64(insn, 32, 32) != 0; | |
332 | return ((fm0 << 1) | fm1); | |
333 | } | |
334 | ||
335 | ||
336 | ||
337 | void | |
338 | sim_engine_run (SIM_DESC sd, | |
339 | int last_cpu_nr, | |
340 | int nr_cpus, | |
341 | int siggnal) | |
342 | { | |
343 | while (1) | |
344 | { | |
345 | address_word cia = PC; | |
346 | address_word nia; | |
347 | l_instruction_word insn = IMEM(cia); | |
348 | int rp_was_set; | |
349 | int rpt_c_was_nonzero; | |
350 | ||
351 | /* Before executing the instruction, we need to test whether or | |
352 | not RPT_C is greater than zero, and save that state for use | |
353 | after executing the instruction. In particular, we need to | |
354 | not care whether the instruction changes RPT_C itself. */ | |
355 | ||
356 | rpt_c_was_nonzero = (RPT_C > 0); | |
357 | ||
358 | /* Before executing the instruction, we need to check to see if | |
359 | we have to decrement RPT_C, the repeat count register. Do this | |
360 | if PC == RPT_E, but only if we are in an active repeat block. */ | |
361 | ||
362 | if (PC == RPT_E && | |
363 | (RPT_C > 0 || PSW_VAL (PSW_RP) != 0)) | |
364 | { | |
365 | RPT_C --; | |
366 | } | |
367 | ||
368 | /* Now execute the instruction at PC */ | |
369 | ||
370 | switch (instruction_type (insn)) | |
371 | { | |
372 | case long_insn: | |
373 | nia = do_long (sd, insn, cia); | |
374 | break; | |
375 | case r_l_insn: | |
376 | /* L <- R */ | |
377 | nia = do_2_short (sd, insn, insn >> 32, integer_unit, cia); | |
378 | break; | |
379 | case l_r_insn: | |
380 | /* L -> R */ | |
381 | nia = do_2_short (sd, insn >> 32, insn, memory_unit, cia); | |
382 | break; | |
383 | case p_insn: | |
384 | nia = do_parallel (sd, insn >> 32, insn, cia); | |
385 | break; | |
386 | default: | |
387 | sim_engine_abort (sd, STATE_CPU (sd, 0), cia, | |
388 | "internal error - engine_run_until_stop - bad switch"); | |
389 | nia = -1; | |
390 | } | |
391 | ||
392 | if (TRACE_ACTION) | |
393 | { | |
394 | if (TRACE_ACTION & TRACE_ACTION_CALL) | |
395 | call_occurred (sd, STATE_CPU (sd, 0), cia, nia); | |
396 | ||
397 | if (TRACE_ACTION & TRACE_ACTION_RETURN) | |
398 | return_occurred (sd, STATE_CPU (sd, 0), cia, nia); | |
399 | ||
400 | TRACE_ACTION = 0; | |
401 | } | |
402 | ||
403 | /* Check now to see if we need to reset the RP bit in the PSW. | |
404 | There are three conditions for this, the RP bit is already | |
405 | set (just a speed optimization), the instruction we just | |
406 | executed is the last instruction in the loop, and the repeat | |
407 | count is currently zero. */ | |
408 | ||
409 | rp_was_set = PSW_VAL (PSW_RP); | |
410 | if (rp_was_set && (PC == RPT_E) && RPT_C == 0) | |
411 | { | |
412 | PSW_SET (PSW_RP, 0); | |
413 | } | |
414 | ||
415 | /* Now update the PC. If we just executed a jump instruction, | |
416 | that takes precedence over everything else. Next comes | |
417 | branching back to RPT_S as a result of a loop. Finally, the | |
418 | default is to simply advance to the next inline | |
419 | instruction. */ | |
420 | ||
421 | if (nia != INVALID_INSTRUCTION_ADDRESS) | |
422 | { | |
423 | PC = nia; | |
424 | } | |
425 | else if (rp_was_set && rpt_c_was_nonzero && (PC == RPT_E)) | |
426 | { | |
427 | PC = RPT_S; | |
428 | } | |
429 | else | |
430 | { | |
431 | PC = cia + 8; | |
432 | } | |
433 | ||
434 | /* Check for DDBT (debugger debug trap) condition. Do this after | |
435 | the repeat block checks so the excursion to the trap handler does | |
436 | not alter looping state. */ | |
437 | ||
438 | if (cia == IBA && PSW_VAL (PSW_DB)) | |
439 | { | |
440 | DPC = PC; | |
441 | PSW_SET (PSW_EA, 1); | |
442 | DPSW = PSW; | |
443 | /* clear all bits in PSW except SM */ | |
444 | PSW &= BIT32 (PSW_SM); | |
445 | /* add DS bit */ | |
446 | PSW |= BIT32 (PSW_DS); | |
447 | /* dispatch to DDBT handler */ | |
448 | PC = 0xfffff128; /* debugger_debug_trap_address */ | |
449 | } | |
450 | ||
451 | /* process any events */ | |
452 | /* FIXME - should L->R or L<-R insns count as two cycles? */ | |
453 | if (sim_events_tick (sd)) | |
454 | { | |
455 | sim_events_process (sd); | |
456 | } | |
457 | } | |
458 | } | |
459 | ||
460 | ||
461 | /* d30v external interrupt handler. | |
462 | ||
463 | Note: This should be replaced by a proper interrupt delivery | |
464 | mechanism. This interrupt mechanism discards later interrupts if | |
465 | an earlier interrupt hasn't been delivered. | |
466 | ||
467 | Note: This interrupt mechanism does not reset its self when the | |
468 | simulator is re-opened. */ | |
469 | ||
470 | void | |
471 | d30v_interrupt_event (SIM_DESC sd, | |
472 | void *data) | |
473 | { | |
474 | if (PSW_VAL (PSW_IE)) | |
475 | /* interrupts not masked */ | |
476 | { | |
477 | /* scrub any pending interrupt */ | |
478 | if (sd->pending_interrupt != NULL) | |
479 | sim_events_deschedule (sd, sd->pending_interrupt); | |
480 | /* deliver */ | |
481 | bPSW = PSW; | |
482 | bPC = PC; | |
483 | PSW = 0; | |
484 | PC = 0xfffff138; /* external interrupt */ | |
485 | do_stack_swap (sd); | |
486 | } | |
487 | else if (sd->pending_interrupt == NULL) | |
488 | /* interrupts masked and no interrupt pending */ | |
489 | { | |
490 | sd->pending_interrupt = sim_events_schedule (sd, 1, | |
491 | d30v_interrupt_event, | |
492 | data); | |
493 | } | |
494 | } | |
495 | ||
496 | #endif |