/* SPIM S20 MIPS simulator. Misc. routines for SPIM. Copyright (C) 1990 by James Larus (larus@cs.wisc.edu). SPIM is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. SPIM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU CC; see the file COPYING. If not, write to James R. Larus, Computer Sciences Department, University of Wisconsin--Madison, 1210 West Dayton Street, Madison, WI 53706, USA or to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Header: /home/aa/projects/spim/public_html/cvsroot/spimsal/spim-utils.c,v 1.5 2001/04/23 23:23:13 spim Exp $ */ #include #include #include "spim.h" #include "inst.h" #include "mem.h" #include "reg.h" #include "sym_tbl.h" #include "version.h" #include "y.tab.h" #include "data.h" /* Imported functions: */ void initialize_scanner (FILE *); int run_spim (mem_addr, register int, int); int yylex (void); int yyparse (void); /* Internal functions: */ static void delete_all_breakpoints (void); static mem_addr copy_int_to_stack (int n); static mem_addr copy_str_to_stack (const char *s); void fix_echo(void); /* Global Variables: */ int bare_machine = 0; /* Non-zero => ignore assembler embellishments to bare hardware */ int quiet = 0; /* Non-zero => no message on traps. */ int memio = 0; /* Non-zero => use memoi mapped I/O */ /* Zero => simulate syscalls */ char *input_file_name; /* Name of file being parsed */ int terminal_out = 0, message_out = 0, console_out = 0; mem_addr program_starting_address = 0; long initial_text_size = TEXT_SIZE; long initial_data_size = DATA_SIZE; long initial_data_limit = DATA_LIMIT; long initial_stack_size = STACK_SIZE; long initial_stack_limit = STACK_LIMIT; long initial_k_text_size = K_TEXT_SIZE; long initial_k_data_size = K_DATA_SIZE; long initial_k_data_limit = K_DATA_LIMIT; /* Initialize or reinitialize the state of the machine. */ void initialize_world (int load_trap_handler) { /* Allocate the floating point registers */ if (FGR == NULL) FPR = (double *) malloc (16 * sizeof (double)); /* Allocate the memory */ make_memory (initial_text_size, initial_data_size, initial_data_limit, initial_stack_size, initial_stack_limit, initial_k_text_size, initial_k_data_size, initial_k_data_limit); initialize_registers (); initialize_symbol_table (); k_text_begins_at_point (K_TEXT_BOT); k_data_begins_at_point (K_DATA_BOT); data_begins_at_point (DATA_BOT); text_begins_at_point (TEXT_BOT); program_starting_address = 0; if (load_trap_handler) { int old_bare = bare_machine; bare_machine = 0; /* Trap handler uses extended machine */ if (read_assembly_file (DEFAULT_TRAP_HANDLER)) fatal_error ("Cannot read trap handler\n"); bare_machine = old_bare; } initialize_scanner (stdin); delete_all_breakpoints (); sprintf(mess_buff, "SPIM %s\n", SPIM_VERSION); write_output (message_out, mess_buff); write_output (message_out, "Copyright 1990-92 by James R. Larus (larus@cs.wisc.edu)\n"); write_output (message_out, "Modified to read SAL code by Scott Kempf (scottk@cs.wisc.edu)\n"); write_output (message_out, "Modifications by UC Berkeley EECS Instructional & Electronics Support\n"); write_output (message_out, "See the file COPYING for license information\n"); } void initialize_registers (void) { bzero (FPR, 16 * sizeof (double)); FGR = (float *) FPR; FWR = (int *) FPR; bzero (R, 32 * sizeof (reg_word)); R[29] = STACK_TOP - BYTES_PER_WORD - 4096; /* Initialize $sp */ PC = 0; Cause = 0; EPC = 0; /* Enable all interrupts if memio is used. */ if (memio) Status_Reg = 0x0000ff01; else Status_Reg = 0; BadVAddr = 0; Context = 0; PRId = 0; } /* Read file NAME, which should contain assembly code. Return zero if successful and non-zero otherwise. */ int read_assembly_file (char *name) { FILE *file = fopen (name, "r");; source_file = 1; if (file == NULL) { sprintf(mess_buff, "Cannot open file: `%s'\n", name); error (mess_buff); return (1); } else { input_file_name = name; initialize_scanner (file); while (yyparse ()) ; fclose (file); flush_local_labels (); return (0); } } mem_addr starting_address (void) { if (PC == 0) { if (program_starting_address) return (program_starting_address); else { program_starting_address = find_symbol_address (DEFAULT_RUN_LOCATION); if (program_starting_address == 0) { sprintf(mess_buff, "Program starting label undefined (%s)\n", DEFAULT_RUN_LOCATION); error(mess_buff); program_starting_address = TEXT_BOT; } return program_starting_address; } } else return (PC); } /* Initialize the SPIM stack with ARGC, ARGV, and ENVP data. */ void initialize_run_stack (int argc, char **argv) { char **p; extern char **environ; int i, j = 0, env_j; mem_addr addrs[10000]; /* Put strings on stack: */ for (p = environ; *p != '\0'; p++) addrs[j++] = copy_str_to_stack (*p); R[REG_A1] = R[29]; env_j = j; for (i = 0; i < argc; i++) addrs[j++] = copy_str_to_stack (argv[i]); R[29] -= 4; /* Leave rest of word empty */ R[29] = R[29] & 0xfffffff8; /* Round down to double word boundary */ /* Build vectors on stack: */ for (i = env_j - 1; i >= 0; i--) copy_int_to_stack (addrs[i]); for (i = j - 1; i >= env_j; i--) copy_int_to_stack (addrs[i]); R[29] = copy_int_to_stack (argc); /* Leave pointing to argc */ R[29] = R[29] & 0xfffffff8; /* Round down to double word boundary */ } static mem_addr copy_str_to_stack (const char *s) { mem_addr str_start; int i = strlen (s); while (i >= 0) { SET_MEM_BYTE (R[29], s[i]); R[29] -= 1; i -= 1; } str_start = (mem_addr) R[29] + 1; R[29] = R[29] & 0xfffffff8; /* Round down to double word boundary */ return (str_start); } static mem_addr copy_int_to_stack (int n) { SET_MEM_WORD (R[29], n); R[29] -= BYTES_PER_WORD; return ((mem_addr) R[29] + BYTES_PER_WORD); } /* Run a program starting at PC for N steps and display each instruction before executing if FLAG is non-zero. If CONTINUE is non-zero, then step through a breakpoint. Return non-zero if breakpoint is encountered. */ #ifndef NOMEMIO #include "sys/ioctl.h" #ifdef TERMIO #include "termio.h" #else #include "sgtty.h" #endif /* TERMIO */ #endif /* NOMEMIO */ int run_program (mem_addr pc, int steps, int display, int cont_bkpt) { warn_undef(); if (cont_bkpt && inst_is_breakpoint (pc)) { mem_addr addr = PC; delete_breakpoint (addr); exception_occurred = 0; run_spim (addr, 1, display); add_breakpoint (addr); steps -= 1; pc = PC; } exception_occurred = 0; #ifndef NOMEMIO /* Set cbreak mode and no echo so simulated code must echo characters. */ /* Note stdin is file number 0. */ if (console_uses_stdin && memio && isatty(0)) { #ifdef TERMIO struct termio arg; ioctl(0, TCGETA, &arg); arg.c_lflag &= ~ICANON; arg.c_lflag &= ~ECHO; ioctl(0, TCSETA, &arg); #else /* TERMIO */ struct sgttyb arg; ioctl(0, TIOCGETP, &arg); arg.sg_flags |= CBREAK; arg.sg_flags &= ~ECHO; ioctl(0, TIOCSETP, &arg); #endif /* TERMIO */ } #endif /* NO MEMIO */ if (!run_spim (pc, steps, display)) /* Can't restart program */ PC = 0; fix_echo(); if (exception_occurred && ((Cause >> 2) & 0x1f == BKPT_EXCPT)) return (1); else return (0); } void fix_echo(void) { #ifndef NOMEMIO if (console_uses_stdin && memio && isatty(0)) { #ifdef TERMIO struct termio arg; ioctl(0, TCGETA, &arg); arg.c_lflag |= ICANON; arg.c_lflag |= ECHO; ioctl(0, TCSETA, &arg); #else /* TERMIO */ struct sgttyb arg; ioctl(0, TIOCGETP, &arg); arg.sg_flags &= ~CBREAK; arg.sg_flags |= ECHO; ioctl(0, TIOCSETP, &arg); #endif /* TERMIO */ } #endif /* NO MEMIO */ } /* Record of where a breakpoint was placed and the instruction previously in memory. */ typedef struct bkptrec { mem_addr addr; instruction *inst; struct bkptrec *next; } bkpt; static bkpt *bkpts = NULL; /* Set a breakpoint at memory location ADDR. */ void add_breakpoint (mem_addr addr) { bkpt *rec = (bkpt *) malloc (sizeof (bkpt)); rec->next = bkpts; rec->addr = addr; if ((rec->inst = set_breakpoint (addr)) != NULL) bkpts = rec; else { if (exception_occurred) { sprintf(mess_buff, "Cannot put a breakpoint at address 0x%08x\n", addr); error (mess_buff); } else { sprintf(mess_buff, "Already have a breakpoint at address 0x%08x\n", addr); error (mess_buff); } free (rec); } } /* Delete all breakpoints at memory location ADDR. */ void delete_breakpoint (mem_addr addr) { bkpt *p, *b; int deleted_one = 0; for (p = NULL, b = bkpts; b != NULL; ) if (b->addr == addr) { bkpt *n; SET_MEM_INST (addr, b->inst); if (p == NULL) bkpts = b->next; else p->next = b->next; n = b->next; free (b); b = n; deleted_one = 1; } else p = b, b = b->next; if (!deleted_one) { sprintf(mess_buff, "No breakpoint to delete at 0x%08x\n", addr); error (mess_buff); } } static void delete_all_breakpoints (void) { bkpt *b, *n; for (b = bkpts, n = NULL; b != NULL; b = n) { n = b->next; free (b); } bkpts = NULL; } /* List all breakpoints. */ void list_breakpoints (void) { bkpt *b; if (bkpts) { for (b = bkpts; b != NULL; b = b->next) { sprintf(mess_buff, "Breakpoint at 0x%08x\n", b->addr); write_output (message_out, mess_buff); } } else { write_output (message_out, "No breakpoints set\n"); } } /* Utility routines */ /* Print the error message then exit. */ void fatal_error (char *string) { fprintf (stderr, "%s", string); exit (-1); /*NOTREACHED*/ } /* Return the entry in the hash TABLE of length LENGTH with key STRING. Return NULL if no such entry exists. */ inst_info * map_string_to_inst_info (register inst_info tbl[], int tbl_len, const register char *id) { register int low = 0; register int hi = tbl_len - 1; while (low <= hi) { register int mid = (low + hi) / 2; const register char *idp = id, *np = tbl[mid].name; while (*idp == *np && *idp != '\0') {idp ++; np ++;} if (*np == '\0' && *idp == '\0') /* End of both strings */ return (& tbl[mid]); else if (*idp > *np) low = mid + 1; else hi = mid - 1; } return NULL; } /* Return the entry in the hash TABLE of length LENGTH with VALUE1 field NUM. Return NULL if no such entry exists. */ inst_info *map_int_to_inst_info (register inst_info tbl[], int tbl_len, register int num) { register int low = 0; register int hi = tbl_len - 1; while (low <= hi) { register int mid = (low + hi) / 2; if (tbl[mid].value1 == num) return (&tbl[mid]); else if (num > tbl[mid].value1) low = mid + 1; else hi = mid - 1; } return NULL; }