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