This commit was generated by cvs2svn to track changes on a CVS vendor
[external/binutils.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, resolve PSW
138              contention in favour of IU. */
139           if(! STATE_CPU (sd, 0)->mvtsys_left_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   STATE_CPU (sd, 0)->mvtsys_left_p = 0;
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;
271   STATE_CPU (sd, 0)->mvtsys_left_p = 0;
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;
288   STATE_CPU (sd, 0)->mvtsys_left_p = 0;
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