b659317c761609b27b953f7ca4b8ff5d8e18453e
[nestedvm.git] / src / org / xwt / mips / Runtime.java
1 // Copyright 2003 Brian Alliet
2 // Based on org.xwt.imp.MIPS by Adam Megacz
3 // Portions Copyright 2003 Adam Megacz
4
5 package org.xwt.mips;
6
7 import org.xwt.mips.util.*;
8 import java.io.*;
9 import java.util.Arrays;
10
11 // FEATURE: Look over the public API, make sure we're exposing a bare minimum
12 // (we might make this an interface in the future)
13
14 public abstract class Runtime implements UsermodeConstants,Registers {
15     /** Pages are 4k in size */
16     protected final int PAGE_SIZE;
17     protected final int PAGE_WORDS;
18     protected final int PAGE_SHIFT;
19     protected final int TOTAL_PAGES;
20     /** This is the upper limit of the pages allocated by the sbrk() syscall. */
21     protected final int BRK_LIMIT;
22     protected final int STACK_BOTTOM;
23
24     /** This is the maximum size of command line arguments */
25     public final static int ARGS_MAX = 1024*1024;
26     
27     /** True if we allow empty pages (_emptyPage) to exist in memory.
28        Empty pages are pages which are allocated by the program but do not contain any
29        data yet (they are all 0s). If empty pages are allowed subclasses must always
30        access main memory with the memRead and memWrite functions */
31     private final boolean allowEmptyPages;
32     /** the "empty page" */
33     private final static int[] _emptyPage = new int[0];
34     
35     protected final static boolean isEmptyPage(int[] page) { return page == _emptyPage; }
36     
37     /** Returns a new empty page (_emptyPage is empty pages are enabled or a new zero'd page) */
38     private final int[] emptyPage() { return allowEmptyPages ? _emptyPage : new int[PAGE_WORDS]; }
39     
40     /** Readable main memory pages */
41     protected final int[][] readPages;
42     /** Writable main memory pages.
43         If the page is writable writePages[x] == readPages[x]; if not writePages[x] == null. */
44     protected final int[][] writePages;
45     
46     /** The current break between the heap and unallocated memory */
47     protected int brkAddr;
48         
49     /** The program's entry point */
50     protected int entryPoint;
51
52     /** The location of the _user_info block (or 0 is there is none) */
53     protected int userInfoBase;
54     protected int userInfoSize;
55     
56     /** The location of the global pointer */
57     protected int gp;
58     
59     /** When the process started */
60     private long startTime;
61     
62     /** State constant: There is no program loaded in memory */
63     public final static int UNINITIALIZED = 0; 
64     /**  Text/Data loaded in memory  */
65     public final static int INITIALIZED = 1;
66     /** Program is executing instructions */
67     public final static int RUNNING = 2;
68     /** Prgram has been started but is paused */
69     public final static int PAUSED = 3;
70     /** Program is executing a callJava() method */
71     public final static int CALLJAVA = 4;
72     /** Program has exited (it cannot currently be restarted) */
73     public final static int DONE = 5;
74         
75     /** The current state (UNINITIALIZED, INITIALIZED, RUNNING, PAUSED, or DONE) */
76     protected int state = UNINITIALIZED;
77     /** @see Runtime#state state */
78     public final int getState() { return state; }
79     
80     /** The exit status if the process (only valid if state==DONE) 
81         @see Runtime#state */
82     protected int exitStatus;
83     public ExecutionException exitException;
84     
85     /** Maximum number of open file descriptors */
86     final static int OPEN_MAX = 256;
87     /** Table containing all open file descriptors. (Entries are null if the fd is not in use */
88     FD[] fds  = new FD[OPEN_MAX];
89         
90     /** Temporary buffer for read/write operations */
91     private byte[] _byteBuf = null;
92     /** Max size of temporary buffer
93         @see Runtime#_byteBuf */
94     private final static int MAX_CHUNK = 15*1024*1024;
95         
96     /** Subclasses should actually execute program in this method. They should continue 
97         executing until state != RUNNING. Only syscall() can modify state. It is safe 
98         to only check the state attribute after a call to syscall() */
99     protected abstract void _execute() throws ExecutionException;
100     
101     /** Subclasses should return the address of the symbol <i>symbol</i> or -1 it it doesn't exits in this method 
102         This method is only required if the call() function is used */
103     protected int lookupSymbol(String symbol) { return -1; }
104     
105     /** Subclasses should returns a CPUState object representing the cpu state */
106     protected abstract CPUState getCPUState();
107     
108     /** Subclasses should set the CPUState to the state held in <i>state</i> */
109     protected abstract void setCPUState(CPUState state);
110
111     static void checkPageSize(int pageSize, int totalPages) throws IllegalArgumentException {
112         if(pageSize < 256) throw new IllegalArgumentException("pageSize too small");
113         if((pageSize&(pageSize-1)) != 0) throw new IllegalArgumentException("pageSize must be a power of two");
114         if((totalPages&(totalPages-1)) != 0) throw new IllegalArgumentException("totalPages must be a power of two");
115         if(totalPages != 1 && totalPages < 256) throw new IllegalArgumentException("totalPages too small");
116         if(totalPages * pageSize < 4*1024*1024) throw new IllegalArgumentException("total memory too small (" + totalPages + "*" + pageSize + ")");
117     }
118     
119     protected Runtime(int pageSize, int totalPages, boolean allowEmptyPages) {
120         this.allowEmptyPages = allowEmptyPages;
121         
122         checkPageSize(pageSize,totalPages);
123         
124         PAGE_SIZE = pageSize;
125         PAGE_WORDS = pageSize>>>2;
126         int pageShift = 0;
127         while(pageSize>>>pageShift != 1) pageShift++;
128         PAGE_SHIFT = pageShift;
129         
130         TOTAL_PAGES = totalPages;
131         
132         readPages = new int[TOTAL_PAGES][];
133         writePages = new int[TOTAL_PAGES][];
134         
135         if(TOTAL_PAGES == 1) {
136             readPages[0] = writePages[0] = new int[PAGE_WORDS];
137             BRK_LIMIT = STACK_BOTTOM = 0;
138         } else {
139             int stackPages = max(TOTAL_PAGES>>>8,(1024*1024)>>>PAGE_SHIFT);
140             STACK_BOTTOM = (TOTAL_PAGES - stackPages) * PAGE_SIZE;
141             // leave some unmapped pages between the stack and the heap
142             BRK_LIMIT = STACK_BOTTOM - 4*PAGE_SIZE;
143         
144             for(int i=0;i<stackPages;i++)
145                 readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage();
146         }
147         
148         addFD(new StdinFD(System.in));
149         addFD(new StdoutFD(System.out));
150         addFD(new StdoutFD(System.err));
151     }
152     
153     /** Copy everything from <i>src</i> to <i>addr</i> initializing uninitialized pages if required. 
154        Newly initalized pages will be marked read-only if <i>ro</i> is set */
155     protected final void initPages(int[] src, int addr, boolean ro) {
156         for(int i=0;i<src.length;) {
157             int page = addr >>> PAGE_SHIFT;
158             int start = (addr&(PAGE_SIZE-1))>>2;
159             int elements = min(PAGE_WORDS-start,src.length-i);
160             if(readPages[page]==null) {
161                 initPage(page,ro);
162             } else if(!ro) {
163                 if(writePages[page] == null) writePages[page] = readPages[page];
164             }
165             System.arraycopy(src,i,readPages[page],start,elements);
166             i += elements;
167             addr += elements*4;
168         }
169     }
170     
171     /** Initialize <i>words</i> of pages starting at <i>addr</i> to 0 */
172     protected final void clearPages(int addr, int words) {
173         for(int i=0;i<words;) {
174             int page = addr >>> PAGE_SHIFT;
175             int start = (addr&(PAGE_SIZE-1))>>2;
176             int elements = min(PAGE_WORDS-start,words-i);
177             if(readPages[page]==null) {
178                 readPages[page] = writePages[page] = emptyPage();
179             } else {
180                 if(writePages[page] == null) writePages[page] = readPages[page];
181                 for(int j=start;j<start+elements;j++) writePages[page][j] = 0;
182             }
183             i += elements;
184             addr += elements*4;
185         }
186     }
187     
188     /** Copies <i>length</i> bytes from the processes memory space starting at
189         <i>addr</i> INTO a java byte array <i>a</i> */
190     public final void copyin(int addr, byte[] buf, int count) throws ReadFaultException {
191         int x=0;
192         if((addr&3)!=0) {
193             int word = memRead(addr&~3);
194             switch(addr&3) {
195                 case 1: buf[x++] = (byte)((word>>>16)&0xff); if(--count==0) break;
196                 case 2: buf[x++] = (byte)((word>>> 8)&0xff); if(--count==0) break;
197                 case 3: buf[x++] = (byte)((word>>> 0)&0xff); if(--count==0) break;
198             }
199             addr = (addr&~3)+4;
200         }
201         if((count&~3) != 0) {
202             int c = count>>>2;
203             int a = addr>>>2;
204             while(c != 0) {
205                 int[] page = readPages[a >>> (PAGE_SHIFT-2)];
206                 if(page == null) throw new ReadFaultException(a<<2);
207                 int index = a&(PAGE_WORDS-1);
208                 int n = min(c,PAGE_WORDS-index);
209                 if(page != _emptyPage) {
210                     for(int i=0;i<n;i++,x+=4) {
211                         int word = page[index+i];
212                         buf[x+0] = (byte)((word>>>24)&0xff); buf[x+1] = (byte)((word>>>16)&0xff);
213                         buf[x+2] = (byte)((word>>> 8)&0xff); buf[x+3] = (byte)((word>>> 0)&0xff);                        
214                     }
215                 }
216                 a += n; c -=n;
217             }
218             addr = a<<2; count &=3;
219         }
220         if(count != 0) {
221             int word = memRead(addr);
222             switch(count) {
223                 case 3: buf[x+2] = (byte)((word>>>8)&0xff);
224                 case 2: buf[x+1] = (byte)((word>>>16)&0xff);
225                 case 1: buf[x+0] = (byte)((word>>>24)&0xff);
226             }
227         }
228     }
229     
230     /** Copies <i>length</i> bytes OUT OF the java array <i>a</i> into the processes memory
231         space at <i>addr</i> */
232     public final void copyout(byte[] buf, int addr, int count) throws FaultException {
233         int x=0;
234         if((addr&3)!=0) {
235             int word = memRead(addr&~3);
236             switch(addr&3) {
237                 case 1: word = (word&0xff00ffff)|((buf[x++]&0xff)<<16); if(--count==0) break;
238                 case 2: word = (word&0xffff00ff)|((buf[x++]&0xff)<< 8); if(--count==0) break;
239                 case 3: word = (word&0xffffff00)|((buf[x++]&0xff)<< 0); if(--count==0) break;
240             }
241             memWrite(addr&~3,word);
242             addr += x;
243         }
244         if((count&~3) != 0) {
245             int c = count>>>2;
246             int a = addr>>>2;
247             while(c != 0) {
248                 int[] page = writePages[a >>> (PAGE_SHIFT-2)];
249                 if(page == null) throw new WriteFaultException(a<<2);
250                 if(page == _emptyPage) page = initPage(a >>> (PAGE_SHIFT-2));
251                 int index = a&(PAGE_WORDS-1);
252                 int n = min(c,PAGE_WORDS-index);
253                 for(int i=0;i<n;i++,x+=4)
254                     page[index+i] = ((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8)|((buf[x+3]&0xff)<<0);
255                 a += n; c -=n;
256             }
257             addr = a<<2; count&=3;
258         }
259         if(count != 0) {
260             int word = memRead(addr);
261             switch(count) {
262                 case 1: word = (word&0x00ffffff)|((buf[x+0]&0xff)<<24); break;
263                 case 2: word = (word&0x0000ffff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16); break;
264                 case 3: word = (word&0x000000ff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8); break;
265             }
266             memWrite(addr,word);
267         }
268     }
269     
270     public final void memcpy(int dst, int src, int count) throws FaultException {
271         if((dst&3) == 0 && (src&3)==0) {
272             if((count&~3) != 0) {
273                 int c = count>>2;
274                 int s = src>>>2;
275                 int d = dst>>>2;
276                 while(c != 0) {
277                     int[] srcPage = readPages[s>>>(PAGE_SHIFT-2)];
278                     if(srcPage == null) throw new ReadFaultException(s<<2);
279                     int[] dstPage = writePages[d>>>(PAGE_SHIFT-2)];
280                     if(dstPage == null) throw new WriteFaultException(d<<2);
281                     int srcIndex = (s&(PAGE_WORDS-1));
282                     int dstIndex = (d&(PAGE_WORDS-1));
283                     int n = min(c,PAGE_WORDS-max(srcIndex,dstIndex));
284                     if(srcPage != _emptyPage) {
285                         if(dstPage == _emptyPage) dstPage = initPage(d>>>(PAGE_SHIFT-2));
286                         System.arraycopy(srcPage,srcIndex,dstPage,dstIndex,n);
287                     } else if(srcPage == _emptyPage && dstPage != _emptyPage) {
288                         Arrays.fill(dstPage,dstIndex,dstIndex+n,0);
289                     }
290                     s += n; d += n; c -= n;
291                 }
292                 src = s<<2; dst = d<<2; count&=3;
293             }
294             if(count != 0) {
295                 int word1 = memRead(src);
296                 int word2 = memRead(dst);
297                 switch(count) {
298                     case 1: memWrite(dst,(word1&0xff000000)|(word2&0x00ffffff)); break;
299                     case 2: memWrite(dst,(word1&0xffff0000)|(word2&0x0000ffff)); break;
300                     case 3: memWrite(dst,(word1&0xffffff00)|(word2&0x000000ff)); break;
301                 }
302             }
303         } else {
304             while(count > 0) {
305                 int n = min(count,MAX_CHUNK);
306                 byte[] buf = byteBuf(n);
307                 copyin(src,buf,n);
308                 copyout(buf,dst,n);
309                 count -= n; src += n; dst += n;
310             }
311         }
312     }
313     
314     public final void memset(int addr, int ch, int count) throws FaultException {
315         int fourBytes = ((ch&0xff)<<24)|((ch&0xff)<<16)|((ch&0xff)<<8)|((ch&0xff)<<0);
316         if((addr&3)!=0) {
317             int word = memRead(addr&~3);
318             switch(addr&3) {
319                 case 1: word = (word&0xff00ffff)|((ch&0xff)<<16); if(--count==0) break;
320                 case 2: word = (word&0xffff00ff)|((ch&0xff)<< 8); if(--count==0) break;
321                 case 3: word = (word&0xffffff00)|((ch&0xff)<< 0); if(--count==0) break;
322             }
323             memWrite(addr&~3,word);
324             addr = (addr&~3)+4;
325         }
326         if((count&~3) != 0) {
327             int c = count>>2;
328             int a = addr>>>2;
329             while(c != 0) {
330                 int[] page = readPages[a>>>(PAGE_SHIFT-2)];
331                 if(page == null) throw new WriteFaultException(a<<2);
332                 int index = (a&(PAGE_WORDS-1));
333                 int n = min(c,PAGE_WORDS-index);
334                 if(page != _emptyPage || ch != 0) {
335                     if(page == _emptyPage) page = initPage(a>>>(PAGE_SHIFT-2));
336                     Arrays.fill(page,index,index+n,fourBytes);
337                 }
338                 a += n; c -= n;
339             }
340             addr = a<<2; count&=3;
341         }
342         if(count != 0) {
343             int word = memRead(addr);
344             switch(count) {
345                 case 1: word = (word&0x00ffffff)|(fourBytes&0xff000000); break;
346                 case 2: word = (word&0x0000ffff)|(fourBytes&0xffff0000); break;
347                 case 3: word = (word&0x000000ff)|(fourBytes&0xffffff00); break;
348             }
349             memWrite(addr,word);
350         }
351     }
352     
353     /** Read a word from the processes memory at <i>addr</i> */
354     public final int memRead(int addr) throws ReadFaultException  {
355         if((addr & 3) != 0) throw new ReadFaultException(addr);
356         return unsafeMemRead(addr);
357     }
358        
359     protected final int unsafeMemRead(int addr) throws ReadFaultException {
360         int page = addr >>> PAGE_SHIFT;
361         int entry = (addr >>> 2) & (PAGE_WORDS-1);
362         try {
363             return readPages[page][entry];
364         } catch(ArrayIndexOutOfBoundsException e) {
365             if(page < 0) throw e; // should never happen
366             if(page >= readPages.length) throw new ReadFaultException(addr);
367             if(readPages[page] != _emptyPage) throw e; // should never happen
368             initPage(page);
369             return 0;
370         } catch(NullPointerException e) {
371             throw new ReadFaultException(addr);
372         }
373     }
374     
375     /** Writes a word to the processes memory at <i>addr</i> */
376     public final void memWrite(int addr, int value) throws WriteFaultException  {
377         if((addr & 3) != 0) throw new WriteFaultException(addr);
378         unsafeMemWrite(addr,value);
379     }
380     
381     protected final void unsafeMemWrite(int addr, int value) throws WriteFaultException {
382         int page = addr >>> PAGE_SHIFT;
383         int entry = (addr>>>2)&(PAGE_WORDS-1);
384         try {
385             writePages[page][entry] = value;
386         } catch(ArrayIndexOutOfBoundsException e) {
387             if(page < 0) throw e;// should never happen
388             if(page >= writePages.length) throw new WriteFaultException(addr);
389             if(readPages[page] != _emptyPage) throw e; // should never happen
390             initPage(page);
391             writePages[page][entry] = value;
392         } catch(NullPointerException e) {
393             throw new WriteFaultException(addr);
394         }
395     }
396     
397     /** Created a new non-empty writable page at page number <i>page</i> */
398     private final int[] initPage(int page) { return initPage(page,false); }
399     /** Created a new non-empty page at page number <i>page</i>. If <i>ro</i> is set the page will be read-only */
400     private final int[] initPage(int page, boolean ro) {
401         int[] buf = new int[PAGE_WORDS];
402         writePages[page] = ro ? null : buf;
403         readPages[page] = buf;
404         return buf;
405     }
406     
407     /** Returns the exit status of the process. (only valid if state == DONE) 
408         @see Runtime#state */
409     public final int exitStatus() {
410         if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state");
411         return exitStatus;
412     }
413         
414     private int addStringArray(String[] strings, int topAddr) {
415         int count = strings.length;
416         int total = 0; /* null last table entry  */
417         for(int i=0;i<count;i++) total += strings[i].length() + 1;
418         if(total >= ARGS_MAX) throw new IllegalArgumentException("arguments/environ too big");
419         total += (count+1)*4;
420         int start = (topAddr - total)&~3;
421         int addr = start + (count+1)*4;
422         int[] table = new int[count+1];
423         try {
424             for(int i=0;i<count;i++) {
425                 byte[] a = getBytes(strings[i]);
426                 table[i] = addr;
427                 copyout(a,addr,a.length);
428                 memset(addr+a.length,0,1);
429                 addr += a.length + 1;
430             }
431             addr=start;
432             for(int i=0;i<count+1;i++) {
433                 memWrite(addr,table[i]);
434                 addr += 4;
435             }
436         } catch(FaultException e) {
437             // should never happen
438             throw new Error(e.toString());
439         }
440         return start;
441     }
442     
443     protected String[] createEnv(String[] extra) { if(extra == null) extra = new String[0]; return extra; }
444     
445     /** Sets word number <i>index</i> in the _user_info table to <i>word</i>
446      * The user_info table is a chunk of memory in the program's memory defined by the
447      * symbol "user_info". The compiler/interpreter automatically determine the size
448      * and location of the user_info table from the ELF symbol table. setUserInfo and
449      * getUserInfo are used to modify the words in the user_info table. */
450     public void setUserInfo(int index, int word) {
451         if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
452         try {
453             memWrite(userInfoBase+index*4,word);
454         } catch(FaultException e) { throw new Error("should never happen: " + e); }
455     }
456     
457     /** Returns the word in the _user_info table entry <i>index</i>
458         @see Runtime#setUserInfo(int,int) setUserInfo */
459     public int getUserInfo(int index) {
460         if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
461         try {
462             return memRead(userInfoBase+index*4);
463         } catch(FaultException e) { throw new Error("should never happen: " + e); }
464     }
465     
466     /** Calls _execute() (subclass's execute()) and catches exceptions */
467     private void __execute() {
468         try {
469             _execute();
470         } catch(FaultException e) {
471             e.printStackTrace();
472             sys_exit(128+11); // SIGSEGV
473             exitException = e;
474         } catch(ExecutionException e) {
475             e.printStackTrace();
476             System.err.println(e);
477             sys_exit(128+4); // SIGILL
478             exitException = e;
479         }
480     }
481     
482     /** Executes the process until the PAUSE syscall is invoked or the process exits. Returns true if the process exited. */
483     public final boolean execute()  {
484         if(state != PAUSED) throw new IllegalStateException("execute() called in inappropriate state");
485         if(startTime == 0) startTime = System.currentTimeMillis();
486         state = RUNNING;
487         __execute();
488         if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")");
489         return state == DONE;
490     }
491     
492     public final int run() { return run(null); }
493     public final int run(String argv0, String[] rest) {
494         String[] args = new String[rest.length+1];
495         System.arraycopy(rest,0,args,1,rest.length);
496         args[0] = argv0;
497         return run(args);
498     }
499     public final int run(String[] args) { return run(args,null); }
500     
501     /** Runs the process until it exits and returns the exit status.
502         If the process executes the PAUSE syscall execution will be paused for 500ms and a warning will be displayed */
503     public final int run(String[] args, String[] env) {
504         start(args,env);
505         for(;;) {
506             if(execute()) break;
507             System.err.println("WARNING: Pause requested while executing run()");
508             try { Thread.sleep(500); } catch(InterruptedException e) { /* noop */ }
509         }
510         return exitStatus();
511     }
512
513     public final void start() { start(null); }
514     public final void start(String[] args) { start(args,null); }
515     
516     /** Initializes the process and prepairs it to be executed with execute() */
517     public final void start(String[] args, String[] environ)  {
518         int sp, argsAddr, envAddr;
519         if(state != INITIALIZED) throw new IllegalStateException("start() called in inappropriate state");
520
521         if(args == null) args = new String[]{getClass().getName()};
522         
523         sp = TOTAL_PAGES*PAGE_SIZE-512;
524         sp = argsAddr = addStringArray(args,sp);
525         sp = envAddr = addStringArray(createEnv(environ),sp);
526         sp &= ~15;
527         
528         CPUState cpuState = new CPUState();
529         cpuState.r[A0] = argsAddr;
530         cpuState.r[A1] = envAddr;
531         cpuState.r[SP] = sp;
532         cpuState.r[RA] = 0xdeadbeef;
533         cpuState.r[GP] = gp;
534         cpuState.pc = entryPoint;
535         setCPUState(cpuState);
536                 
537         state = PAUSED;
538         
539         _start();
540     }
541     
542     /** Hook for subclasses to do their own startup */
543     protected void _start() { /* noop */ }
544     
545     public final int call(String sym) throws CallException { return call(sym,0,0,0,0,0,0,0); }
546     public final int call(String sym, int a0) throws CallException  { return call(sym,a0,0,0,0,0,0,0); }
547     public final int call(String sym, int a0, int a1) throws CallException  { return call(sym,a0,a1,0,0,0,0,0); }
548     public final int call(String sym, int a0, int a1, int a2) throws CallException  { return call(sym,a0,a1,a2,0,0,0,0); }
549     public final int call(String sym, int a0, int a1, int a2, int a3) throws CallException  { return call(sym,a0,a1,a2,a3,0,0,0); }
550     public final int call(String sym, int a0, int a1, int a2, int a3, int a4) throws CallException  { return call(sym,a0,a1,a2,a3,a4,0,0); }
551     public final int call(String sym, int a0, int a1, int a2, int a3, int a4, int a5) throws CallException  { return call(sym,a0,a1,a2,a3,a4,a5,0); }
552     
553     /** Calls a function in the process with the given arguments */
554     public final int call(String sym, int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws CallException {
555         int func = lookupSymbol(sym);
556         if(func == -1) throw new CallException(sym + " not found");
557         int helper = lookupSymbol("_call_helper");
558         if(helper == -1) throw new CallException("_call_helper not found");
559         return call(helper,func,a0,a1,a2,a3,a4,a5,a6);
560     }
561     
562     /** Executes the code at <i>addr</i> in the process setting A0-A3 and S0-S3 to the given arguments
563         and returns the contents of V1 when the the pause syscall is invoked */
564     public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) {
565         if(state != PAUSED && state != CALLJAVA) throw new IllegalStateException("call() called in inappropriate state");
566         int oldState = state;
567         CPUState saved = getCPUState();
568         CPUState cpustate = new CPUState();
569         cpustate.r[SP] = saved.r[SP]&~15;
570         cpustate.r[RA] = 0xdeadbeef;
571         cpustate.r[A0] = a0;
572         cpustate.r[A1] = a1;
573         cpustate.r[A2] = a2;
574         cpustate.r[A3] = a3;
575         cpustate.r[S0] = s0;
576         cpustate.r[S1] = s1;
577         cpustate.r[S2] = s2;
578         cpustate.r[S3] = s3;
579         cpustate.r[GP] = gp;
580         cpustate.pc = addr;
581         
582         state = RUNNING;
583
584         setCPUState(cpustate);
585         __execute();
586         cpustate = getCPUState();
587         setCPUState(saved);
588
589         if(state != PAUSED)
590             System.out.println("WARNING: Process exit()ed while servicing a call() request");
591         else
592             state = oldState;
593         
594         return cpustate.r[V1];
595     }
596     
597     // FEATURE: This is ugly - we should have some kind of way  to specify a callback rather than requiring subclassing
598     protected int callJava(int a, int b, int c, int d) {
599         System.err.println("WARNING: Default implementation of callJava() called with args " + toHex(a) + "," + toHex(b) + "," + toHex(c) + "," + toHex(d));
600         return 0;
601     }
602     
603     /** Determines if the process can access <i>fileName</i>. The default implementation simply logs 
604         the request and allows it */
605     protected boolean allowFileAccess(String fileName, boolean write) {
606         //System.err.println("Allowing " + (write?"write":"read-only") + " access to " + fileName);
607         return true;
608     }
609     
610     /** Allocated an entry in the FileDescriptor table for <i>fd</i> and returns the number.
611         Returns -1 if the table is full. This can be used by subclasses to use custom file
612         descriptors */
613     public int addFD(FD fd) {
614         int i;
615         for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
616         if(i==OPEN_MAX) return -1;
617         fds[i] = fd;
618         return i;
619     }
620
621     /** Closes file descriptor <i>fdn</i> and removes it from the file descriptor table */
622     public boolean closeFD(int fdn) {
623         if(fdn < 0 || fdn >= OPEN_MAX) return false;
624         if(fds[fdn] == null) return false;
625         fds[fdn].close();
626         fds[fdn] = null;        
627         return true;
628     }
629
630     // FEATURE: These should be pulled in from UsermodeConstants but fcntl.h is hard to parse
631     public static final int RD_ONLY = 0;
632     public static final int WR_ONLY = 1;
633     public static final int RDWR = 2;
634     
635     public static final int O_CREAT = 0x0200;
636     public static final int O_EXCL = 0x0800;
637     public static final int O_APPEND = 0x0008;
638     public static final int O_TRUNC = 0x0400;
639     public static final int O_NONBLOCK = 0x4000;
640     
641     // FEATURE: Lots of duplicate code between this and UnixRuntime.HostFS.open()
642     protected FD open(String path, int flags, int mode) throws IOException {
643         final File f = new File(path);
644         // NOTE: createNewFile is a Java2 function
645         if((flags & (O_EXCL|O_CREAT)) == (O_EXCL|O_CREAT))
646             if(!f.createNewFile()) throw new ErrnoException(EEXIST);
647         if(!f.exists() && (flags&O_CREAT) == 0) return null;
648         if(f.isDirectory()) return null;
649         final SeekableFile sf = new SeekableFile(path,mode!=RD_ONLY);
650         if((flags&O_TRUNC)!=0) sf.setLength(0);
651         return new SeekableFD(sf,flags) {
652             protected FStat _fstat() { return new HostFStat(f) {
653                 public int size() {
654                     try { return sf.length(); } catch(IOException e) { return 0; }
655                 }
656             };}
657         };
658     }
659     
660     /** The open syscall */
661     private int sys_open(int addr, int flags, int mode) {
662         if((flags & O_NONBLOCK) != 0) {
663             System.err.println("WARNING: O_NONBLOCK not supported");
664             return -EOPNOTSUPP;
665         }
666
667         try {
668             FD fd = open(cstring(addr),flags,mode);
669             if(fd == null) return -ENOENT;
670             int fdn = addFD(fd);
671             if(fdn == -1) {
672                 fd.close();
673                 return -ENFILE;
674             }
675             return fdn;
676         }
677         catch(ErrnoException e) { return -e.errno; }
678         catch(FileNotFoundException e) {
679             if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
680             return -ENOENT;
681         }
682         catch(IOException e) { return -EIO; }
683         catch(FaultException e) { return -EFAULT; }
684     }
685
686     /** The write syscall */
687     private int sys_write(int fdn, int addr, int count) {
688         count = Math.min(count,MAX_CHUNK);
689         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
690         if(fds[fdn] == null || !fds[fdn].writable()) return -EBADFD;
691         try {
692             byte[] buf = byteBuf(count);
693             copyin(addr,buf,count);
694             return fds[fdn].write(buf,0,count);
695         } catch(FaultException e) {
696             System.err.println(e);
697             return -EFAULT;
698         } catch(IOException e) {
699             // FEATURE: We should support signals and send a SIGPIPE
700             if(e.getMessage().equals("Pipe closed")) return sys_exit(128+13);
701             System.err.println(e);
702             return -EIO;
703         }
704     }
705
706     /** The read syscall */
707     private int sys_read(int fdn, int addr, int count) {
708         count = Math.min(count,MAX_CHUNK);
709         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
710         if(fds[fdn] == null || !fds[fdn].readable()) return -EBADFD;
711         try {
712             byte[] buf = byteBuf(count);
713             int n = fds[fdn].read(buf,0,count);
714             copyout(buf,addr,n);
715             return n;
716         } catch(FaultException e) {
717             System.err.println(e);
718             return -EFAULT;
719         } catch(IOException e) {
720             System.err.println(e);
721             return -EIO;
722         }
723     }
724     
725     /** The close syscall */
726     private int sys_close(int fdn) {
727         return closeFD(fdn) ? 0 : -EBADFD;
728     }
729
730     
731     /** The seek syscall */
732     private int sys_lseek(int fdn, int offset, int whence) {
733         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
734         if(fds[fdn] == null) return -EBADFD;
735         if(whence != SEEK_SET && whence !=  SEEK_CUR && whence !=  SEEK_END) return -EINVAL;
736         try {
737             int n = fds[fdn].seek(offset,whence);
738             return n < 0 ? -ESPIPE : n;
739         } catch(IOException e) {
740             return -ESPIPE;
741         }
742     }
743     
744     /** The stat/fstat syscall helper */
745     int stat(FStat fs, int addr) {
746         try {
747             memWrite(addr+0,(fs.dev()<<16)|(fs.inode()&0xffff)); // st_dev (top 16), // st_ino (bottom 16)
748             memWrite(addr+4,((fs.type()&0xf000))|(fs.mode()&0xfff)); // st_mode
749             memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16)
750             memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16)
751             memWrite(addr+16,fs.size()); // st_size
752             memWrite(addr+20,fs.atime()); // st_atime
753             // memWrite(addr+24,0) // st_spare1
754             memWrite(addr+28,fs.mtime()); // st_mtime
755             // memWrite(addr+32,0) // st_spare2
756             memWrite(addr+36,fs.ctime()); // st_ctime
757             // memWrite(addr+40,0) // st_spare3
758             memWrite(addr+44,fs.blksize()); // st_bklsize;
759             memWrite(addr+48,fs.blocks()); // st_blocks
760             // memWrite(addr+52,0) // st_spare4[0]
761             // memWrite(addr+56,0) // st_spare4[1]
762         } catch(FaultException e) {
763             System.err.println(e);
764             return -EFAULT;
765         }
766         return 0;
767     }
768     
769     /** The fstat syscall */
770     private int sys_fstat(int fdn, int addr) {
771         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
772         if(fds[fdn] == null) return -EBADFD;
773         return stat(fds[fdn].fstat(),addr);
774     }
775     
776     /*
777     struct timeval {
778     long tv_sec;
779     long tv_usec;
780     };
781     */
782     private int sys_gettimeofday(int timevalAddr, int timezoneAddr) {
783         long now = System.currentTimeMillis();
784         int tv_sec = (int)(now / 1000);
785         int tv_usec = (int)((now%1000)*1000);
786         try {
787             memWrite(timevalAddr+0,tv_sec);
788             memWrite(timevalAddr+4,tv_usec);
789             return 0;
790         } catch(FaultException e) {
791             return -EFAULT;
792         }
793     }
794     
795     private int sys_sleep(int sec) {
796         if(sec < 0) sec = Integer.MAX_VALUE;
797         try {
798             Thread.sleep((long)sec*1000);
799             return 0;
800         } catch(InterruptedException e) {
801             return -1;
802         }
803     }
804     
805     /*
806       #define _CLOCKS_PER_SEC_ 1000
807       #define    _CLOCK_T_    unsigned long
808     struct tms {
809       clock_t   tms_utime;
810       clock_t   tms_stime;
811       clock_t   tms_cutime;    
812       clock_t   tms_cstime;
813     };*/
814    
815     private int sys_times(int tms) {
816         long now = System.currentTimeMillis();
817         int userTime = (int)((now - startTime)/16);
818         int sysTime = (int)((now - startTime)/16);
819         
820         try {
821             if(tms!=0) {
822                 memWrite(tms+0,userTime);
823                 memWrite(tms+4,sysTime);
824                 memWrite(tms+8,userTime);
825                 memWrite(tms+12,sysTime);
826             }
827         } catch(FaultException e) {
828             return -EFAULT;
829         }
830         return (int)now;
831     }
832     
833     private int sys_sysconf(int n) {
834         switch(n) {
835             case _SC_CLK_TCK: return 1000;
836             default:
837                 System.err.println("WARNING: Attempted to use unknown sysconf key: " + n);
838                 return -EINVAL;
839         }
840     }
841     
842     /** The sbrk syscall. This can also be used by subclasses to allocate memory.
843         <i>incr</i> is how much to increase the break by */
844     public int sbrk(int incr) {
845         if(incr < 0) return -ENOMEM;
846         if(incr==0) return brkAddr;
847         incr = (incr+3)&~3;
848         int oldBrk = brkAddr;
849         int newBrk = oldBrk + incr;
850         if(TOTAL_PAGES == 1) {
851             CPUState state = getCPUState();
852             if(newBrk >= state.r[SP] - 65536) {
853                 System.err.println("WARNING: brk too close to stack pointer");
854                 return -ENOMEM;
855             }
856         } else if(newBrk >= BRK_LIMIT) {
857             System.err.println("WARNING: Hit BRK_LIMIT");
858             return -ENOMEM;
859         }
860         if(TOTAL_PAGES != 1) {
861             try {
862                 for(int i=(oldBrk+PAGE_SIZE-1)>>>PAGE_SHIFT;i<((newBrk+PAGE_SIZE-1)>>>PAGE_SHIFT);i++)
863                     readPages[i] = writePages[i] = emptyPage();
864             } catch(OutOfMemoryError e) {
865                 System.err.println("WARNING: Caught OOM Exception in sbrk: " + e);
866                 return -ENOMEM;
867             }
868         }
869         brkAddr = newBrk;
870         return oldBrk;
871     }
872
873     /** The getpid syscall */
874     private int sys_getpid() { return getPid(); }
875     protected int getPid() { return 1; }
876     
877     private int sys_calljava(int a, int b, int c, int d) {
878         if(state != RUNNING) throw new IllegalStateException("wound up calling sys_calljava while not in RUNNING");
879         state = CALLJAVA;
880         int ret = callJava(a,b,c,d);
881         state = RUNNING;
882         return ret;
883     }
884         
885     private int sys_pause() {
886         state = PAUSED;
887         return 0;
888     }
889     
890     private int sys_getpagesize() { return TOTAL_PAGES == 1 ? 4096 : PAGE_SIZE; }
891     
892     private int sys_isatty(int fdn) {
893         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
894         if(fds[fdn] == null) return -EBADFD;
895         return fds[fdn].isatty() ? 1 : 0;
896     }
897
898     
899     /** Hook for subclasses to do something when the process exits (MUST set state = DONE) */
900     protected void _exit() { state = DONE; }
901     private int sys_exit(int status) {
902         exitStatus = status;
903         for(int i=0;i<fds.length;i++) if(fds[i] != null) sys_close(i);
904         _exit();
905         return 0;
906     }
907        
908     private int sys_fcntl(int fdn, int cmd, int arg) {
909         final int F_DUPFD = 0;
910         final int F_GETFL = 3;
911         int i;
912             
913         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
914         if(fds[fdn] == null) return -EBADFD;
915         FD fd = fds[fdn];
916         
917         switch(cmd) {
918             case F_DUPFD:
919                 if(arg < 0 || arg >= OPEN_MAX) return -EINVAL;
920                 for(i=arg;i<OPEN_MAX;i++) if(fds[i]==null) break;
921                 if(i==OPEN_MAX) return -EMFILE;
922                 fds[i] = fd.dup();
923                 return 0;
924             case F_GETFL:
925                 int flags = 0;
926                 if(fd.writable() && fd.readable())  flags = 2;
927                 else if(fd.writable()) flags = 1;
928                 return flags;
929             default:
930                 System.err.println("WARNING: Unknown fcntl command: " + cmd);
931                 return -ENOSYS;
932         }
933     }
934             
935     /** The syscall dispatcher.
936         The should be called by subclasses when the syscall instruction is invoked.
937         <i>syscall</i> should be the contents of V0 and <i>a</i>, <i>b</i>, <i>c</i>, and <i>d</i> should be 
938         the contenst of A0, A1, A2, and A3. The call MAY change the state
939         @see Runtime#state state */
940     protected int syscall(int syscall, int a, int b, int c, int d) {
941         switch(syscall) {
942             case SYS_null: return 0;
943             case SYS_exit: return sys_exit(a);
944             case SYS_pause: return sys_pause();
945             case SYS_write: return sys_write(a,b,c);
946             case SYS_fstat: return sys_fstat(a,b);
947             case SYS_sbrk: return sbrk(a);
948             case SYS_open: return sys_open(a,b,c);
949             case SYS_close: return sys_close(a);
950             case SYS_read: return sys_read(a,b,c);
951             case SYS_lseek: return sys_lseek(a,b,c);
952             case SYS_getpid: return sys_getpid();
953             case SYS_calljava: return sys_calljava(a,b,c,d);
954             case SYS_gettimeofday: return sys_gettimeofday(a,b);
955             case SYS_sleep: return sys_sleep(a);
956             case SYS_times: return sys_times(a);
957             case SYS_getpagesize: return sys_getpagesize();
958             case SYS_isatty: return sys_isatty(a);
959             case SYS_fcntl: return sys_fcntl(a,b,c);
960             case SYS_sysconf: return sys_sysconf(a);
961
962             case SYS_kill:
963             case SYS_fork:
964             case SYS_pipe:
965             case SYS_dup2:
966             case SYS_waitpid:
967             case SYS_stat:
968             case SYS_mkdir:
969             case SYS_getcwd:
970             case SYS_chdir:
971                 System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")");
972                 return -ENOSYS;
973             default:
974                 System.err.println("Attempted to use unknown syscall: " + syscall);
975                 return -ENOSYS;
976         }
977     }
978     
979     public int xmalloc(int size) { int p=malloc(size); if(p==0) throw new RuntimeException("malloc() failed"); return p; }
980     public int xrealloc(int addr,int newsize) { int p=realloc(addr,newsize); if(p==0) throw new RuntimeException("realloc() failed"); return p; }
981     public int realloc(int addr, int newsize) { try { return call("realloc",addr,newsize); } catch(CallException e) { return 0; } }
982     public int malloc(int size) { try { return call("malloc",size); } catch(CallException e) { return 0; } }
983     public void free(int p) { try { if(p!=0) call("free",p); } catch(CallException e) { /*noop*/ } }
984     
985     /** Helper function to create a cstring in main memory */
986     public int strdup(String s) {
987         byte[] a;
988         if(s == null) s = "(null)";
989         byte[] a2 = getBytes(s);
990         a = new byte[a2.length+1];
991         System.arraycopy(a2,0,a,0,a2.length);
992         int addr = malloc(a.length);
993         if(addr == 0) return 0;
994         try {
995             copyout(a,addr,a.length);
996         } catch(FaultException e) {
997             free(addr);
998             return 0;
999         }
1000         return addr;
1001     }
1002     
1003     /** Helper function to read a cstring from main memory */
1004     public String cstring(int addr) throws ReadFaultException {
1005         StringBuffer sb = new StringBuffer();
1006         for(;;) {
1007             int word = memRead(addr&~3);
1008             switch(addr&3) {
1009                 case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++;
1010                 case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++;
1011                 case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++;
1012                 case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++;
1013             }
1014         }
1015     }
1016     
1017     /** File Descriptor class */
1018     public static abstract class FD {
1019         private int refCount = 1;
1020     
1021         /** returns true if the fd is readable */
1022         public boolean readable() { return false; }
1023         /** returns true if the fd is writable */
1024         public boolean writable() { return false; }
1025         
1026         /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */
1027         public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1028         /** Write. Should return the number of bytes written or throw an IOException on error */
1029         public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1030
1031         /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */
1032         public int seek(int n, int whence)  throws IOException  { return -1; }
1033         
1034         /** Should return true if this is a tty */
1035         public boolean isatty() { return false; }
1036         
1037         private FStat cachedFStat = null;
1038         public final FStat fstat() {
1039             if(cachedFStat == null) cachedFStat = _fstat(); 
1040             return cachedFStat;
1041         }
1042         
1043         protected abstract FStat _fstat();
1044         
1045         /** Closes the fd */
1046         public final void close() { if(--refCount==0) _close(); }
1047         protected void _close() { /* noop*/ }
1048         
1049         FD dup() { refCount++; return this; }
1050     }
1051         
1052     /** FileDescriptor class for normal files */
1053     public abstract static class SeekableFD extends FD {
1054         private final int flags;
1055         private final SeekableData data;
1056         public boolean readable() { return (flags&3) != WR_ONLY; }
1057         public boolean writable() { return (flags&3) != RD_ONLY; }
1058         
1059         SeekableFD(SeekableData data, int flags) { this.data = data; this.flags = flags; }
1060         
1061         protected abstract FStat _fstat();
1062
1063         public int seek(int n, int whence) throws IOException {
1064             switch(whence) {
1065                 case SEEK_SET: break;
1066                 case SEEK_CUR: n += data.pos(); break;
1067                 case SEEK_END: n += data.length(); break;
1068                 default: return -1;
1069             }
1070             data.seek(n);
1071             return n;
1072         }
1073         
1074         public int write(byte[] a, int off, int length) throws IOException {
1075             // NOTE: There is race condition here but we can't fix it in pure java
1076             if((flags&O_APPEND) != 0) seek(0,SEEK_END);
1077             return data.write(a,off,length);
1078         }
1079         
1080         public int read(byte[] a, int off, int length) throws IOException {
1081             int n = data.read(a,off,length);
1082             return n < 0 ? 0 : n;
1083         }
1084         
1085         protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } }        
1086     }
1087     
1088     public static class OutputStreamFD extends FD {
1089         private OutputStream os;
1090         public boolean writable() { return true; }
1091         public OutputStreamFD(OutputStream os) { this.os = os; }
1092         public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; }
1093         public void _close() { try { os.close(); } catch(IOException e) { /*ignore*/ }  }
1094         public FStat _fstat() { return new FStat(); }
1095     }
1096     
1097     public static class InputStreamFD extends FD {
1098         private InputStream is;
1099         public boolean readable() { return true; }
1100         public InputStreamFD(InputStream is) { this.is = is; }
1101         public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
1102         public void _close() { try { is.close(); } catch(IOException e) { /*ignore*/ } }
1103         public FStat _fstat() { return new FStat(); }
1104     }
1105     
1106     protected static class StdinFD extends InputStreamFD {
1107         public StdinFD(InputStream is) { super(is); }
1108         public void _close() { /* noop */ }
1109         public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1110         public boolean isatty() { return true; }
1111     }
1112     protected static class StdoutFD extends OutputStreamFD {
1113         public StdoutFD(OutputStream os) { super(os); }
1114         public void _close() { /* noop */ }
1115         public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1116         public boolean isatty() { return true; }
1117     }
1118     
1119     public static class FStat {
1120         public static final int S_IFIFO = 0010000;
1121         public static final int S_IFCHR = 0020000;
1122         public static final int S_IFDIR = 0040000;
1123         public static final int S_IFREG = 0100000;
1124         
1125         public int dev() { return -1; }
1126         // FEATURE: inode numbers are calculated inconsistently throught the runtime
1127         public int inode() { return hashCode() & 0xfffff; }
1128         public int mode() { return 0; }
1129         public int type() { return S_IFIFO; }
1130         public int nlink() { return 0; }
1131         public int uid() { return 0; }
1132         public int gid() { return 0; }
1133         public int size() { return 0; }
1134         public int atime() { return 0; }
1135         public int mtime() { return 0; }
1136         public int ctime() { return 0; }
1137         public int blksize() { return 512; }
1138         public int blocks() { return (size()+blksize()-1)/blksize(); }        
1139     }
1140     
1141     protected static class HostFStat extends FStat {
1142         private final File f;
1143         private final boolean executable; 
1144         public HostFStat(File f) {
1145             this.f = f;
1146             String name = f.getName();
1147             // FEATURE: This is ugly.. maybe we should do a file(1) type check
1148             executable = name.endsWith(".mips") || name.endsWith(".sh");
1149         }
1150         public int dev() { return 1; }
1151         public int inode() { return f.getName().hashCode() & 0xffff; }
1152         public int type() { return f.isDirectory() ? S_IFDIR : S_IFREG; }
1153         public int nlink() { return 1; }
1154         public int mode() {
1155             int mode = 0;
1156             boolean canread = f.canRead();
1157             if(canread && (executable || f.isDirectory())) mode |= 0111;
1158             if(canread) mode |= 0444;
1159             if(f.canWrite()) mode |= 0222;
1160             return mode;
1161         }
1162         public int size() { return (int) f.length(); }
1163         public int mtime() { return (int)(f.lastModified()/1000); }
1164     }
1165     
1166     // Exceptions
1167     public class ReadFaultException extends FaultException {
1168         public ReadFaultException(int addr) { super(addr); }
1169     }
1170     public class WriteFaultException extends FaultException {
1171         public WriteFaultException(int addr) { super(addr); }
1172     }
1173     public abstract class FaultException extends ExecutionException {
1174         public int addr;
1175         public FaultException(int addr) { super("fault at: " + toHex(addr)); this.addr = addr; }
1176     }
1177     public static class ExecutionException extends Exception {
1178         private String message = "(null)";
1179         private String location = "(unknown)";
1180         public ExecutionException() { /* noop */ }
1181         public ExecutionException(String s) { if(s != null) message = s; }
1182         void setLocation(String s) { location = s == null ? "(unknown)" : s; }
1183         public final String getMessage() { return message + " at " + location; }
1184     }
1185     public static class CallException extends Exception {
1186         public CallException(String s) { super(s); }
1187     }
1188     
1189     protected static class ErrnoException extends IOException {
1190         public int errno;
1191         public ErrnoException(int errno) { super("Errno: " + errno); this.errno = errno; }
1192     }
1193     
1194     // CPU State
1195     protected static class CPUState {
1196         public CPUState() { /* noop */ }
1197         /* GPRs */
1198         public int[] r = new int[32];
1199         /* Floating point regs */
1200         public int[] f = new int[32];
1201         public int hi, lo;
1202         public int fcsr;
1203         public int pc;
1204     }
1205     
1206     // Null pointer check helper function
1207     protected final void nullPointerCheck(int addr) throws ExecutionException {
1208         if(TOTAL_PAGES==1 ? addr < 65536 : (addr>>>PAGE_SHIFT) < 16)
1209             throw new ExecutionException("Attempted to dereference a null pointer " + toHex(addr));
1210     }
1211     
1212     // Utility functions
1213     private byte[] byteBuf(int size) {
1214         if(_byteBuf==null) _byteBuf = new byte[size];
1215         else if(_byteBuf.length < size)
1216             _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)];
1217         return _byteBuf;
1218     }
1219     
1220     protected static String getSystemProperty(String key) {
1221         try {
1222             return System.getProperty(key);
1223         } catch(SecurityException e) {
1224             return null;
1225         }
1226     }
1227     
1228     /** Decode an packed string.. FEATURE: document this better */
1229     protected static final int[] decodeData(String s, int words) {
1230         if(s.length() % 8 != 0) throw new IllegalArgumentException("string length must be a multiple of 8");
1231         if((s.length() / 8) * 7 < words*4) throw new IllegalArgumentException("string isn't big enough");
1232         int[] buf = new int[words];
1233         int prev = 0, left=0;
1234         for(int i=0,n=0;n<words;i+=8) {
1235             long l = 0;
1236             for(int j=0;j<8;j++) { l <<= 7; l |= s.charAt(i+j) & 0x7f; }
1237             if(left > 0) buf[n++] = prev | (int)(l>>>(56-left));
1238             if(n < words) buf[n++] = (int) (l >>> (24-left));
1239             left = (left + 8) & 0x1f;
1240             prev = (int)(l << left);
1241         }
1242         return buf;
1243     }
1244     
1245     protected static byte[] getBytes(String s) {
1246         try {
1247             return s.getBytes("ISO-8859-1");
1248         } catch(UnsupportedEncodingException e) {
1249             return null; // should never happen
1250         }
1251     }
1252     
1253     protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
1254     protected final static int min(int a, int b) { return a < b ? a : b; }
1255     protected final static int max(int a, int b) { return a > b ? a : b; }
1256 }