1 /* SPIM S20 MIPS simulator.
2    Misc. routines for SPIM.
3 
4    Copyright (c) 1990-2010, James R. Larus.
5    All rights reserved.
6 
7    Redistribution and use in source and binary forms, with or without modification,
8    are permitted provided that the following conditions are met:
9 
10    Redistributions of source code must retain the above copyright notice,
11    this list of conditions and the following disclaimer.
12 
13    Redistributions in binary form must reproduce the above copyright notice,
14    this list of conditions and the following disclaimer in the documentation and/or
15    other materials provided with the distribution.
16 
17    Neither the name of the James R. Larus nor the names of its contributors may be
18    used to endorse or promote products derived from this software without specific
19    prior written permission.
20 
21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 
33 
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <string.h>
37 #include <stdarg.h>
38 
39 #include "spim.h"
40 #include "string-stream.h"
41 #include "spim-utils.h"
42 #include "inst.h"
43 #include "data.h"
44 #include "reg.h"
45 #include "mem.h"
46 #include "scanner.h"
47 #include "parser.h"
48 #include "y.tab.h"
49 #include "run.h"
50 #include "sym-tbl.h"
51 
52 
53 /* Internal functions: */
54 
55 static mem_addr copy_int_to_stack (int n);
56 static mem_addr copy_str_to_stack (char *s);
57 static void delete_all_breakpoints ();
58 
59 
60 int exception_occurred;
61 
62 mem_addr program_starting_address = 0;
63 
64 int initial_text_size = TEXT_SIZE;
65 
66 int initial_data_size = DATA_SIZE;
67 
68 mem_addr initial_data_limit = DATA_LIMIT;
69 
70 int initial_stack_size = STACK_SIZE;
71 
72 mem_addr initial_stack_limit = STACK_LIMIT;
73 
74 int initial_k_text_size = K_TEXT_SIZE;
75 
76 int initial_k_data_size = K_DATA_SIZE;
77 
78 mem_addr initial_k_data_limit = K_DATA_LIMIT;
79 
80 
81 
82 /* Initialize or reinitialize the state of the machine. */
83 
84 void
initialize_world(char * exception_file_names)85 initialize_world (char* exception_file_names)
86 {
87   /* Allocate the floating point registers */
88   if (FGR == NULL)
89     FPR = (double *) xmalloc (FPR_LENGTH * sizeof (double));
90   /* Allocate the memory */
91   make_memory (initial_text_size,
92 	       initial_data_size, initial_data_limit,
93 	       initial_stack_size, initial_stack_limit,
94 	       initial_k_text_size,
95 	       initial_k_data_size, initial_k_data_limit);
96   initialize_registers ();
97   program_starting_address = 0;
98   initialize_inst_tables ();
99   initialize_symbol_table ();
100   k_text_begins_at_point (K_TEXT_BOT);
101   k_data_begins_at_point (K_DATA_BOT);
102   data_begins_at_point (DATA_BOT);
103   text_begins_at_point (TEXT_BOT);
104 
105   if (exception_file_names != NULL)
106     {
107       int old_bare = bare_machine;
108       int old_accept = accept_pseudo_insts;
109       char *filename;
110       char *files;
111 
112       /* Save machine state */
113       bare_machine = 0;	       /* Exception handler uses extended machine */
114       accept_pseudo_insts = 1;
115 
116       /* strtok modifies the string, so we must back up the string prior to use. */
117       if ((files = strdup (exception_file_names)) == NULL)
118          fatal_error ("Insufficient memory to complete.\n");
119 
120       for (filename = strtok (files, ";"); filename != NULL; filename = strtok (NULL, ";"))
121          {
122             if (read_assembly_file (filename))
123                fatal_error ("Cannot read exception handler: %s\n", filename);
124 
125             write_output (message_out, "Loaded: %s\n", filename);
126          }
127 
128       free (files);
129 
130       /* Restore machine state */
131       bare_machine = old_bare;
132       accept_pseudo_insts = old_accept;
133 
134       if (!bare_machine)
135       {
136 	(void)make_label_global ("main"); /* In case .globl main forgotten */
137 	(void)record_label ("main", 0, 0);
138       }
139     }
140   initialize_scanner (stdin);
141   delete_all_breakpoints ();
142 }
143 
144 
145 void
write_startup_message()146 write_startup_message ()
147 {
148   write_output (message_out, "SPIM %s\n", SPIM_VERSION);
149   write_output (message_out, "Copyright 1990-2010, James R. Larus.\n");
150   write_output (message_out, "All Rights Reserved.\n");
151 #ifdef WIN32
152   write_output (message_out, "DOS and Windows ports by David A. Carley.\n");
153   write_output (message_out, "Copyright 1997, Morgan Kaufmann Publishers, Inc.\n");
154 #endif
155   write_output (message_out, "See the file README for a full copyright notice.\n");
156 }
157 
158 
159 
160 void
initialize_registers()161 initialize_registers ()
162 {
163   memclr (FPR, FPR_LENGTH * sizeof (double));
164   FGR = (float *) FPR;
165   FWR = (int *) FPR;
166 
167   memclr (R, R_LENGTH * sizeof (reg_word));
168   R[REG_SP] = STACK_TOP - BYTES_PER_WORD - 4096; /* Initialize $sp */
169   HI = LO = 0;
170   PC = 0;
171 
172   CP0_BadVAddr = 0;
173   CP0_Count = 0;
174   CP0_Compare = 0;
175   CP0_Status = (CP0_Status_CU & 0x30000000) | CP0_Status_IM | CP0_Status_UM;
176   CP0_Cause = 0;
177   CP0_EPC = 0;
178 #ifdef BIGENDIAN
179   CP0_Config =  CP0_Config_BE;
180 #else
181   CP0_Config = 0;
182 #endif
183 
184   FIR = FIR_W | FIR_D | FIR_S;	/* Word, double, & single implemented */
185   FCSR = 0x0;
186   FCCR = 0x0;
187   FEXR = 0x0;
188   FENR = 0x0;
189 }
190 
191 
192 /* Read file NAME, which should contain assembly code. Return zero if
193    successful and non-zero otherwise. */
194 
195 int
read_assembly_file(char * name)196 read_assembly_file (char *name)
197 {
198   FILE *file = fopen (name, "rt");
199 
200   if (file == NULL)
201     {
202       error ("Cannot open file: `%s'\n", name);
203       return (1);
204     }
205   else
206     {
207       initialize_scanner (file);
208       initialize_parser (name);
209 
210       while (!yyparse ()) ;
211 
212       fclose (file);
213       flush_local_labels (!parse_error_occurred);
214       end_of_assembly_file ();
215       return (0);
216     }
217 }
218 
219 
220 mem_addr
starting_address()221 starting_address ()
222 {
223   if (PC == 0)
224     {
225       if (program_starting_address != 0)
226 	return (program_starting_address);
227       else
228 	return (program_starting_address
229 		= find_symbol_address (DEFAULT_RUN_LOCATION));
230     }
231   else
232     return (PC);
233 }
234 
235 
236 /* Initialize the SPIM stack with ARGC, ARGV, and ENVP data. */
237 
238 #ifdef _MSC_VER
239 #define environ	_environ
240 #endif
241 
242 void
initialize_run_stack(int argc,char ** argv)243 initialize_run_stack (int argc, char **argv)
244 {
245   char **p;
246   extern char **environ;
247   int i, j = 0, env_j;
248   mem_addr addrs[10000];
249 
250 
251   R[REG_SP] = STACK_TOP - 1; /* Initialize $sp */
252 
253   /* Put strings on stack: */
254   /* env: */
255   for (p = environ; *p != NULL; p++)
256     addrs[j++] = copy_str_to_stack (*p);
257   env_j = j;
258 
259   /* argv; */
260   for (i = 0; i < argc; i++)
261     addrs[j++] = copy_str_to_stack (argv[i]);
262 
263   /* Align stack pointer for word-size data */
264   R[REG_SP] = R[REG_SP] & ~3;	/* Round down to nearest word */
265   R[REG_SP] -= BYTES_PER_WORD;	/* First free word on stack */
266   R[REG_SP] = R[REG_SP] & ~7;	/* Double-word align stack-pointer*/
267 
268   /* Build vectors on stack: */
269   /* env: */
270   (void)copy_int_to_stack (0);	/* Null-terminate vector */
271   for (i = env_j - 1; i >= 0; i--)
272     R[REG_A2] = copy_int_to_stack (addrs[i]);
273 
274   /* argv: */
275   (void)copy_int_to_stack (0);	/* Null-terminate vector */
276   for (i = j - 1; i >= env_j; i--)
277     R[REG_A1] = copy_int_to_stack (addrs[i]);
278 
279   /* argc: */
280   R[REG_A0] = argc;
281   set_mem_word (R[REG_SP], argc); /* Leave argc on stack */
282 }
283 
284 
285 static mem_addr
copy_str_to_stack(char * s)286 copy_str_to_stack (char *s)
287 {
288   int i = strlen (s);
289   while (i >= 0)
290     {
291       set_mem_byte (R[REG_SP], s[i]);
292       R[REG_SP] -= 1;
293       i -= 1;
294     }
295   return ((mem_addr) R[REG_SP] + 1); /* Leaves stack pointer byte-aligned!! */
296 }
297 
298 
299 static mem_addr
copy_int_to_stack(int n)300 copy_int_to_stack (int n)
301 {
302   set_mem_word (R[REG_SP], n);
303   R[REG_SP] -= BYTES_PER_WORD;
304   return ((mem_addr) R[REG_SP] + BYTES_PER_WORD);
305 }
306 
307 
308 /* Run a program starting at PC for N steps and display each
309    instruction before executing if FLAG is non-zero.  If CONTINUE is
310    non-zero, then step through a breakpoint.  Return non-zero if
311    breakpoint is encountered. */
312 
313 int
run_program(mem_addr pc,int steps,int display,int cont_bkpt)314 run_program (mem_addr pc, int steps, int display, int cont_bkpt)
315 {
316   if (cont_bkpt && inst_is_breakpoint (pc))
317     {
318       mem_addr addr = PC == 0 ? pc : PC;
319 
320       delete_breakpoint (addr);
321       exception_occurred = 0;
322       run_spim (addr, 1, display);
323       add_breakpoint (addr);
324       steps -= 1;
325       pc = PC;
326     }
327 
328   exception_occurred = 0;
329   if (!run_spim (pc, steps, display))
330       /* Can't restart program */
331       PC = 0;
332   if (exception_occurred && CP0_ExCode == ExcCode_Bp)
333   {
334       /* Turn off EXL bit, so subsequent interrupts set EPC since the break is
335       handled by SPIM code, not MIPS code. */
336       CP0_Status &= ~CP0_Status_EXL;
337       return (1);
338   }
339   else
340     return (0);
341 }
342 
343 
344 /* Record of where a breakpoint was placed and the instruction previously
345    in memory. */
346 
347 typedef struct bkptrec
348 {
349   mem_addr addr;
350   instruction *inst;
351   struct bkptrec *next;
352 } bkpt;
353 
354 
355 static bkpt *bkpts = NULL;
356 
357 
358 /* Set a breakpoint at memory location ADDR. */
359 
360 void
add_breakpoint(mem_addr addr)361 add_breakpoint (mem_addr addr)
362 {
363   bkpt *rec = (bkpt *) xmalloc (sizeof (bkpt));
364 
365   rec->next = bkpts;
366   rec->addr = addr;
367 
368   if ((rec->inst = set_breakpoint (addr)) != NULL)
369     bkpts = rec;
370   else
371     {
372       if (exception_occurred)
373 	error ("Cannot put a breakpoint at address 0x%08x\n", addr);
374       else
375 	error ("No instruction to breakpoint at address 0x%08x\n", addr);
376       free (rec);
377     }
378 }
379 
380 
381 /* Delete all breakpoints at memory location ADDR. */
382 
383 void
delete_breakpoint(mem_addr addr)384 delete_breakpoint (mem_addr addr)
385 {
386   bkpt *p, *b;
387   int deleted_one = 0;
388 
389   for (p = NULL, b = bkpts; b != NULL; )
390     if (b->addr == addr)
391       {
392 	bkpt *n;
393 
394 	set_mem_inst (addr, b->inst);
395 	if (p == NULL)
396 	  bkpts = b->next;
397 	else
398 	  p->next = b->next;
399 	n = b->next;
400 	free (b);
401 	b = n;
402 	deleted_one = 1;
403       }
404     else
405       p = b, b = b->next;
406   if (!deleted_one)
407     error ("No breakpoint to delete at 0x%08x\n", addr);
408 }
409 
410 
411 static void
delete_all_breakpoints()412 delete_all_breakpoints ()
413 {
414   bkpt *b, *n;
415 
416   for (b = bkpts, n = NULL; b != NULL; b = n)
417     {
418       n = b->next;
419       free (b);
420     }
421   bkpts = NULL;
422 }
423 
424 
425 /* List all breakpoints. */
426 
427 void
list_breakpoints()428 list_breakpoints ()
429 {
430   bkpt *b;
431 
432   if (bkpts)
433     for (b = bkpts;  b != NULL; b = b->next)
434       write_output (message_out, "Breakpoint at 0x%08x\n", b->addr);
435   else
436     write_output (message_out, "No breakpoints set\n");
437 }
438 
439 
440 
441 /* Utility routines */
442 
443 
444 /* Return the entry in the linear TABLE of length LENGTH with key STRING.
445    TABLE must be sorted on the key field.
446    Return NULL if no such entry exists. */
447 
448 name_val_val *
map_string_to_name_val_val(name_val_val tbl[],int tbl_len,char * id)449 map_string_to_name_val_val (name_val_val tbl[], int tbl_len, char *id)
450 {
451   int low = 0;
452   int hi = tbl_len - 1;
453 
454   while (low <= hi)
455     {
456       int mid = (low + hi) / 2;
457       char *idp = id, *np = tbl[mid].name;
458 
459       while (*idp == *np && *idp != '\0') {idp ++; np ++;}
460 
461       if (*np == '\0' && *idp == '\0') /* End of both strings */
462 	return (& tbl[mid]);
463       else if (*idp > *np)
464 	low = mid + 1;
465       else
466 	hi = mid - 1;
467     }
468 
469   return NULL;
470 }
471 
472 
473 /* Return the entry in the linear TABLE of length LENGTH with VALUE1 field NUM.
474    TABLE must be sorted on the VALUE1 field.
475    Return NULL if no such entry exists. */
476 
477 name_val_val *
map_int_to_name_val_val(name_val_val tbl[],int tbl_len,int num)478 map_int_to_name_val_val (name_val_val tbl[], int tbl_len, int num)
479 {
480   int low = 0;
481   int hi = tbl_len - 1;
482 
483   while (low <= hi)
484     {
485       int mid = (low + hi) / 2;
486 
487       if (tbl[mid].value1 == num)
488 	return (&tbl[mid]);
489       else if (num > tbl[mid].value1)
490 	low = mid + 1;
491       else
492 	hi = mid - 1;
493     }
494 
495   return NULL;
496 }
497 
498 
499 #ifdef NEED_VSPRINTF
500 char *
vsprintf(str,fmt,args)501 vsprintf (str, fmt, args)
502      char *str,*fmt;
503      va_list *args;
504 {
505   FILE _strbuf;
506 
507   _strbuf._flag = _IOWRT+_IOSTRG;
508   _strbuf._ptr = str;
509   _strbuf._cnt = 32767;
510   _doprnt(fmt, args, &_strbuf);
511   putc('\0', &_strbuf);
512   return(str);
513 }
514 #endif
515 
516 
517 #ifdef NEED_STRTOL
518 unsigned long
strtol(const char * str,const char ** eptr,int base)519 strtol (const char* str, const char** eptr, int base)
520 {
521   long result;
522 
523   if (base != 0 && base != 16)
524     fatal_error ("SPIM's strtol only works for base 16 (not base %d)\n", base);
525   if (*str == '0' && (*(str + 1) == 'x' || *(str + 1) == 'X'))
526     {
527       str += 2;
528       sscanf (str, "%lx", &result);
529     }
530   else if (base == 16)
531     {
532       sscanf (str, "%lx", &result);
533     }
534   else
535     {
536       sscanf (str, "%ld", &result);
537     }
538   return (result);
539 }
540 #endif
541 
542 #ifdef NEED_STRTOUL
543 unsigned long
strtoul(const char * str,char ** eptr,int base)544 strtoul (const char* str, char** eptr, int base)
545 {
546   unsigned long result;
547 
548   if (base != 0 && base != 16)
549     fatal_error ("SPIM's strtoul only works for base 16 (not base %d)\n", base);
550   if (*str == '0' && (*(str + 1) == 'x' || *(str + 1) == 'X'))
551     {
552       str += 2;
553       sscanf (str, "%lx", &result);
554     }
555   else if (base == 16)
556     {
557       sscanf (str, "%lx", &result);
558     }
559   else
560     {
561       sscanf (str, "%ld", &result);
562     }
563   return (result);
564 }
565 #endif
566 
567 
568 char *
str_copy(char * str)569 str_copy (char *str)
570 {
571   return (strcpy (xmalloc (strlen (str) + 1), str));
572 }
573 
574 
575 void *
xmalloc(int size)576 xmalloc (int size)
577 {
578   void *x = (void *) malloc (size);
579 
580   if (x == 0)
581     fatal_error ("Out of memory at request for %d bytes.\n");
582   return (x);
583 }
584 
585 
586 /* Allocate a zero'ed block of storage. */
587 
588 void *
zmalloc(int size)589 zmalloc (int size)
590 {
591   void *z = (void *) malloc (size);
592 
593   if (z == 0)
594     fatal_error ("Out of memory at request for %d bytes.\n");
595 
596   memclr (z, size);
597   return (z);
598 }
599