OSDN Git Service

Initial revision
[pf3gnuchains/pf3gnuchains4x.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