/* SPIM S20 MIPS simulator. Code to create, maintain and access memory. 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/mem.c,v 1.1.1.1 1999/08/21 19:31:25 brg Exp $ */ #include #include "spim.h" #include "inst.h" #include "mem.h" #include "reg.h" /* Local functions: */ static void free_instructions(instruction **inst ,int n); /* Local variables: */ static long data_size_limit, stack_size_limit, k_data_size_limit; /* Memory is allocated in five chunks: text, data, stack, kernel text, and kernel data. There is also an I/O area which is not allocated. The arrays are independent and have different semantics. text is allocated from 0x400000 up and only contains INSTRUCTIONs. It does not expand. data is allocated from 0x10000000 up. It can be extended by the SBRK system call. Programs can only read and write this segment. stack grows from 0x7fffefff down. It is automatically extended. Programs can only read and write this segment. I/O is from 0xbfff000 up to 0xbfffffff and contains I/O buffers. Note that this is _not_ implemented with and array k_text is like text, except its is allocated from 0x80000000 up. k_data is like data, but is allocated from 0x90000000 up. Both kernel text and kernel data can only be accessed in kernel mode. */ void make_memory (long text_size, long data_size, long data_limit, long stack_size, long stack_limit, long k_text_size, long k_data_size, long k_data_limit) { if (data_size <= 65536) fatal_error ("Data segment must be larger than 64K\n"); if (text_seg == NULL) text_seg = (instruction **) malloc (sizeof (instruction *) * text_size / BYTES_PER_WORD); else free_instructions (text_seg, (text_top - TEXT_BOT) / BYTES_PER_WORD); if (text_seg == NULL) fatal_error ("malloc failed in make_memory\n"); bzero (text_seg, sizeof (instruction *) * text_size / BYTES_PER_WORD); text_top = TEXT_BOT + text_size; if (data_seg == NULL) data_seg = (mem_word *) malloc (sizeof (mem_word) * data_size / BYTES_PER_WORD); if (data_seg == NULL) fatal_error ("malloc failed in make_memory\n"); bzero (data_seg, sizeof (instruction *) * data_size / BYTES_PER_WORD); data_seg_b = (BYTE_TYPE *) data_seg; data_seg_h = (short *) data_seg; data_top = DATA_BOT + data_size; data_size_limit = data_limit; if (stack_seg == NULL) stack_seg = (mem_word *) malloc (sizeof (mem_word) * stack_size / BYTES_PER_WORD); if (stack_seg == NULL) fatal_error ("malloc failed in make_memory\n"); bzero (stack_seg, sizeof (instruction *) * stack_size / BYTES_PER_WORD); stack_seg_b = (BYTE_TYPE *) stack_seg; stack_seg_h = (short *) stack_seg; stack_bot = STACK_TOP - stack_size; stack_size_limit = stack_limit; if (k_text_seg == NULL) k_text_seg = (instruction **) malloc (sizeof (instruction *) * k_text_size / BYTES_PER_WORD); else free_instructions (k_text_seg, (k_text_top - K_TEXT_BOT) / BYTES_PER_WORD); if (k_text_seg == NULL) fatal_error ("malloc failed in make_memory\n"); bzero (k_text_seg, sizeof (instruction *) * k_text_size / BYTES_PER_WORD); k_text_top = K_TEXT_BOT + k_text_size; if (k_data_seg == NULL) k_data_seg = (mem_word *) malloc (sizeof (mem_word) * k_data_size / BYTES_PER_WORD); if (k_data_seg == NULL) fatal_error ("malloc failed in make_memory\n"); bzero (k_data_seg, sizeof (instruction *) * k_data_size / BYTES_PER_WORD); k_data_seg_b = (BYTE_TYPE *) k_data_seg; k_data_seg_h = (short *) k_data_seg; k_data_top = K_DATA_BOT + k_data_size; k_data_size_limit = k_data_limit; text_modified = 0; data_modified = 0; } /* Free the storage used by the old instructions in memory. */ static void free_instructions (register instruction **inst, int n) { for ( ; n > 0; n --, inst ++) if (*inst) { if (EXPR (*inst)) free (EXPR (*inst)); free (*inst); } } /* Expand the data segment by adding N bytes. */ void expand_data (long addl_bytes) { long old_size = data_top - DATA_BOT; long new_size = old_size + addl_bytes; register mem_word *p; if (addl_bytes < 0 || (source_file && new_size > data_size_limit)) { sprintf(mess_buff, "Can't expand data segment by %d bytes to %d bytes\n", addl_bytes, new_size); error (mess_buff); sprintf(mess_buff, "Use -ldata # with # > %d\n", new_size); run_error (mess_buff); } data_seg = (mem_word *) realloc (data_seg, new_size); if (data_seg == NULL) fatal_error ("realloc failed in expand_data\n"); data_seg_b = (BYTE_TYPE *) data_seg; data_seg_h = (short *) data_seg; for (p = data_seg + old_size / BYTES_PER_WORD; p < data_seg + new_size / BYTES_PER_WORD; ) *p ++ = 0; data_top += addl_bytes; } /* Expand the stack segment by adding N bytes. Can't use REALLOC since it copies from bottom of memory blocks and stack grows down from top of its block. */ void expand_stack (long addl_bytes) { long old_size = STACK_TOP - stack_bot; long new_size = old_size + addl_bytes; mem_word *new_seg; register mem_word *po, *pn; if (addl_bytes < 0 || (source_file && new_size > stack_size_limit)) { sprintf(mess_buff, "Can't expand stack segment by %d bytes to %d bytes\n", addl_bytes, new_size); error (mess_buff); sprintf(mess_buff, "Use -lstack # with # > %d\n", new_size); run_error (mess_buff); } new_seg = (mem_word *) malloc (new_size); if (new_seg == NULL) fatal_error ("malloc failed in expand_stack\n"); po = stack_seg + old_size / BYTES_PER_WORD; pn = new_seg + new_size / BYTES_PER_WORD; for ( ; po >= stack_seg ; ) *pn -- = *po --; for ( ; pn >= new_seg ; ) *pn -- = 0; stack_seg = new_seg; stack_seg_b = (BYTE_TYPE *) stack_seg; stack_seg_h = (short *) stack_seg; stack_bot -= addl_bytes; } /* Expand the kernel data segment by adding N bytes. */ void expand_k_data (long addl_bytes) { long old_size = k_data_top - K_DATA_BOT; long new_size = old_size + addl_bytes; register mem_word *p; if (addl_bytes < 0 || (source_file && new_size > k_data_size_limit)) { sprintf(mess_buff, "Can't expand kernel data segment by %d bytes to %d bytes\n", addl_bytes, new_size); error (mess_buff); sprintf(mess_buff, "Use -lkdata # with # > %d\n", new_size); run_error (mess_buff); } k_data_seg = (mem_word *) realloc (k_data_seg, new_size); if (k_data_seg == NULL) fatal_error ("realloc failed in expand_k_data\n"); k_data_seg_b = (BYTE_TYPE *) k_data_seg; k_data_seg_h = (short *) k_data_seg; for (p = k_data_seg + old_size / BYTES_PER_WORD; p < k_data_seg + new_size / BYTES_PER_WORD; ) *p ++ = 0; k_data_top += addl_bytes; } instruction * funny_text_read (mem_addr addr) { mem_word bits; READ_MEM_WORD (bits, addr); return (inst_decode (bits)); } void funny_text_write (mem_addr addr, instruction *inst) { mem_word bits = inst_encode (inst); SET_MEM_WORD (addr, bits); } mem_word bad_mem_read (mem_addr addr, int mask, mem_word *dest) { if (addr & mask) RAISE_EXCEPTION (ADDRL_EXCPT, BadVAddr = addr) else if (addr >= TEXT_BOT && addr < text_top) { if (mask != 0x3) run_error ("SPIM restriction: Can only read text segment by words\n"); else return (inst_encode (text_seg [(addr - TEXT_BOT) >> 2])); } else if (addr > data_top && addr < stack_bot /* If more than 16 MB below stack, probably is bad data ref */ && addr > stack_bot - 16*1000*K) { /* Grow stack segment */ expand_stack (stack_bot - addr + 4); *dest = 0; /* Newly allocated memory */ } else /* Address out of range */ RAISE_EXCEPTION (DBUS_EXCPT, BadVAddr = addr) return (0); } void bad_mem_write (mem_addr addr, mem_word value, int mask) { if (addr & mask) /* Unaligned address fault */ RAISE_EXCEPTION (ADDRS_EXCPT, BadVAddr = addr) else if (addr >= TEXT_BOT && addr < text_top) { if (mask != 0x3) run_error ("SPIM restriction: Can only write text segment by words\n"); else text_seg [(addr - TEXT_BOT) >> 2] = inst_decode (value); } else if (addr > data_top && addr < stack_bot /* If more than 16 MB below stack, probably is bad data ref */ && addr > stack_bot - 16*1000*K) { /* Grow stack segment */ expand_stack (stack_bot - addr + 4); if (addr >= stack_bot) { if (mask == 0) stack_seg_b [addr - stack_bot] = value; else if (mask == 1) stack_seg_h [addr - stack_bot] = value; else stack_seg [addr - stack_bot] = value; } else RAISE_EXCEPTION (DBUS_EXCPT, BadVAddr = addr) } else /* Address out of range */ RAISE_EXCEPTION (DBUS_EXCPT, BadVAddr = addr) } void print_mem (mem_addr addr) { long value; if (TEXT_BOT <= addr && addr < text_top) print_inst (addr); else if (DATA_BOT <= addr && addr < data_top) { READ_MEM_WORD (value, addr); sprintf(mess_buff, "Data seg @ 0x%08x (%d) = 0x%08x (%d)\n", addr, addr, value, value); write_output (message_out, mess_buff); } else if (stack_bot <= addr && addr < STACK_TOP) { READ_MEM_WORD (value, addr); sprintf(mess_buff, "Stack seg @ 0x%08x (%d) = 0x%08x (%d)\n", addr, addr, value, value); write_output (message_out, mess_buff); } else if (K_TEXT_BOT <= addr && addr < k_text_top) print_inst (addr); else if (K_DATA_BOT <= addr && addr < k_data_top) { READ_MEM_WORD (value, addr); sprintf(mess_buff, "Kernel Data seg @ 0x%08x (%d) = 0x%08x (%d)\n", addr, addr, value, value); write_output (message_out, mess_buff); } else { sprintf(mess_buff, "Address 0x%08x (%d) to print_mem is out of bounds\n", addr, addr); error (mess_buff); } }