6675d59c30d483e0d090c5f98f62ac7b97b31efe
[nestedvm.git] / src / org / ibex / nestedvm / 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.ibex.nestedvm;
6
7 import org.ibex.nestedvm.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     /** Duplicates the file descriptor <i>fdn</i> and returns the new fs */
631     public int dupFD(int fdn) {
632                 int i;
633                 if(fdn < 0 || fdn >= OPEN_MAX) return -1;
634                 if(fds[fdn] == null) return -1;
635                 for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
636         if(i==OPEN_MAX) return -1;
637         fds[i] = fds[fdn].dup();
638         return i;
639     }
640
641     // FEATURE: These should be pulled in from UsermodeConstants but fcntl.h is hard to parse
642     public static final int RD_ONLY = 0;
643     public static final int WR_ONLY = 1;
644     public static final int RDWR = 2;
645     
646     public static final int O_CREAT = 0x0200;
647     public static final int O_EXCL = 0x0800;
648     public static final int O_APPEND = 0x0008;
649     public static final int O_TRUNC = 0x0400;
650     public static final int O_NONBLOCK = 0x4000;
651     
652     // FEATURE: Lots of duplicate code between this and UnixRuntime.HostFS.open()
653     protected FD open(String path, int flags, int mode) throws IOException {
654         final File f = new File(path);
655         // NOTE: createNewFile is a Java2 function
656         if((flags & (O_EXCL|O_CREAT)) == (O_EXCL|O_CREAT))
657             if(!f.createNewFile()) throw new ErrnoException(EEXIST);
658         if(!f.exists() && (flags&O_CREAT) == 0) return null;
659         if(f.isDirectory()) return null;
660         final Seekable.File sf = new Seekable.File(path,mode!=RD_ONLY);
661         if((flags&O_TRUNC)!=0) sf.setLength(0);
662         return new SeekableFD(sf,flags) {
663             protected FStat _fstat() { return new HostFStat(f) {
664                 public int size() {
665                     try { return sf.length(); } catch(IOException e) { return 0; }
666                 }
667             };}
668         };
669     }
670     
671     /** The open syscall */
672     private int sys_open(int addr, int flags, int mode) {
673         if((flags & O_NONBLOCK) != 0) {
674             System.err.println("WARNING: O_NONBLOCK not supported");
675             return -EOPNOTSUPP;
676         }
677
678         try {
679             FD fd = open(cstring(addr),flags,mode);
680             if(fd == null) return -ENOENT;
681             int fdn = addFD(fd);
682             if(fdn == -1) {
683                 fd.close();
684                 return -ENFILE;
685             }
686             return fdn;
687         }
688         catch(ErrnoException e) { return -e.errno; }
689         catch(FileNotFoundException e) {
690             if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
691             return -ENOENT;
692         }
693         catch(IOException e) { return -EIO; }
694         catch(FaultException e) { return -EFAULT; }
695     }
696
697     /** The write syscall */
698     private int sys_write(int fdn, int addr, int count) {
699         count = Math.min(count,MAX_CHUNK);
700         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
701         if(fds[fdn] == null || !fds[fdn].writable()) return -EBADFD;
702         try {
703             byte[] buf = byteBuf(count);
704             copyin(addr,buf,count);
705             return fds[fdn].write(buf,0,count);
706         } catch(FaultException e) {
707             System.err.println(e);
708             return -EFAULT;
709         } catch(IOException e) {
710             // FEATURE: We should support signals and send a SIGPIPE
711             if(e.getMessage().equals("Pipe closed")) return sys_exit(128+13);
712             System.err.println(e);
713             return -EIO;
714         }
715     }
716
717     /** The read syscall */
718     private int sys_read(int fdn, int addr, int count) {
719         count = Math.min(count,MAX_CHUNK);
720         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
721         if(fds[fdn] == null || !fds[fdn].readable()) return -EBADFD;
722         try {
723             byte[] buf = byteBuf(count);
724             int n = fds[fdn].read(buf,0,count);
725             copyout(buf,addr,n);
726             return n;
727         } catch(FaultException e) {
728             System.err.println(e);
729             return -EFAULT;
730         } catch(IOException e) {
731             System.err.println(e);
732             return -EIO;
733         }
734     }
735     
736     /** The close syscall */
737     private int sys_close(int fdn) {
738         return closeFD(fdn) ? 0 : -EBADFD;
739     }
740
741     
742     /** The seek syscall */
743     private int sys_lseek(int fdn, int offset, int whence) {
744         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
745         if(fds[fdn] == null) return -EBADFD;
746         if(whence != SEEK_SET && whence !=  SEEK_CUR && whence !=  SEEK_END) return -EINVAL;
747         try {
748             int n = fds[fdn].seek(offset,whence);
749             return n < 0 ? -ESPIPE : n;
750         } catch(IOException e) {
751             return -ESPIPE;
752         }
753     }
754     
755     /** The stat/fstat syscall helper */
756     int stat(FStat fs, int addr) {
757         try {
758             memWrite(addr+0,(fs.dev()<<16)|(fs.inode()&0xffff)); // st_dev (top 16), // st_ino (bottom 16)
759             memWrite(addr+4,((fs.type()&0xf000))|(fs.mode()&0xfff)); // st_mode
760             memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16)
761             memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16)
762             memWrite(addr+16,fs.size()); // st_size
763             memWrite(addr+20,fs.atime()); // st_atime
764             // memWrite(addr+24,0) // st_spare1
765             memWrite(addr+28,fs.mtime()); // st_mtime
766             // memWrite(addr+32,0) // st_spare2
767             memWrite(addr+36,fs.ctime()); // st_ctime
768             // memWrite(addr+40,0) // st_spare3
769             memWrite(addr+44,fs.blksize()); // st_bklsize;
770             memWrite(addr+48,fs.blocks()); // st_blocks
771             // memWrite(addr+52,0) // st_spare4[0]
772             // memWrite(addr+56,0) // st_spare4[1]
773         } catch(FaultException e) {
774             System.err.println(e);
775             return -EFAULT;
776         }
777         return 0;
778     }
779     
780     /** The fstat syscall */
781     private int sys_fstat(int fdn, int addr) {
782         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
783         if(fds[fdn] == null) return -EBADFD;
784         return stat(fds[fdn].fstat(),addr);
785     }
786     
787     /*
788     struct timeval {
789     long tv_sec;
790     long tv_usec;
791     };
792     */
793     private int sys_gettimeofday(int timevalAddr, int timezoneAddr) {
794         long now = System.currentTimeMillis();
795         int tv_sec = (int)(now / 1000);
796         int tv_usec = (int)((now%1000)*1000);
797         try {
798             memWrite(timevalAddr+0,tv_sec);
799             memWrite(timevalAddr+4,tv_usec);
800             return 0;
801         } catch(FaultException e) {
802             return -EFAULT;
803         }
804     }
805     
806     private int sys_sleep(int sec) {
807         if(sec < 0) sec = Integer.MAX_VALUE;
808         try {
809             Thread.sleep((long)sec*1000);
810             return 0;
811         } catch(InterruptedException e) {
812             return -1;
813         }
814     }
815     
816     /*
817       #define _CLOCKS_PER_SEC_ 1000
818       #define    _CLOCK_T_    unsigned long
819     struct tms {
820       clock_t   tms_utime;
821       clock_t   tms_stime;
822       clock_t   tms_cutime;    
823       clock_t   tms_cstime;
824     };*/
825    
826     private int sys_times(int tms) {
827         long now = System.currentTimeMillis();
828         int userTime = (int)((now - startTime)/16);
829         int sysTime = (int)((now - startTime)/16);
830         
831         try {
832             if(tms!=0) {
833                 memWrite(tms+0,userTime);
834                 memWrite(tms+4,sysTime);
835                 memWrite(tms+8,userTime);
836                 memWrite(tms+12,sysTime);
837             }
838         } catch(FaultException e) {
839             return -EFAULT;
840         }
841         return (int)now;
842     }
843     
844     private int sys_sysconf(int n) {
845         switch(n) {
846             case _SC_CLK_TCK: return 1000;
847             default:
848                 System.err.println("WARNING: Attempted to use unknown sysconf key: " + n);
849                 return -EINVAL;
850         }
851     }
852     
853     /** The sbrk syscall. This can also be used by subclasses to allocate memory.
854         <i>incr</i> is how much to increase the break by */
855     public int sbrk(int incr) {
856         if(incr < 0) return -ENOMEM;
857         if(incr==0) return brkAddr;
858         incr = (incr+3)&~3;
859         int oldBrk = brkAddr;
860         int newBrk = oldBrk + incr;
861         if(TOTAL_PAGES == 1) {
862             CPUState state = getCPUState();
863             if(newBrk >= state.r[SP] - 65536) {
864                 System.err.println("WARNING: brk too close to stack pointer");
865                 return -ENOMEM;
866             }
867         } else if(newBrk >= BRK_LIMIT) {
868             System.err.println("WARNING: Hit BRK_LIMIT");
869             return -ENOMEM;
870         }
871         if(TOTAL_PAGES != 1) {
872             try {
873                 for(int i=(oldBrk+PAGE_SIZE-1)>>>PAGE_SHIFT;i<((newBrk+PAGE_SIZE-1)>>>PAGE_SHIFT);i++)
874                     readPages[i] = writePages[i] = emptyPage();
875             } catch(OutOfMemoryError e) {
876                 System.err.println("WARNING: Caught OOM Exception in sbrk: " + e);
877                 return -ENOMEM;
878             }
879         }
880         brkAddr = newBrk;
881         return oldBrk;
882     }
883
884     /** The getpid syscall */
885     private int sys_getpid() { return getPid(); }
886     protected int getPid() { return 1; }
887     
888     private int sys_calljava(int a, int b, int c, int d) {
889         if(state != RUNNING) throw new IllegalStateException("wound up calling sys_calljava while not in RUNNING");
890         state = CALLJAVA;
891         int ret = callJava(a,b,c,d);
892         state = RUNNING;
893         return ret;
894     }
895         
896     private int sys_pause() {
897         state = PAUSED;
898         return 0;
899     }
900     
901     private int sys_getpagesize() { return TOTAL_PAGES == 1 ? 4096 : PAGE_SIZE; }
902     
903     private int sys_isatty(int fdn) {
904         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
905         if(fds[fdn] == null) return -EBADFD;
906         return fds[fdn].isatty() ? 1 : 0;
907     }
908
909     
910     /** Hook for subclasses to do something when the process exits (MUST set state = DONE) */
911     protected void _exit() { state = DONE; }
912     private int sys_exit(int status) {
913         exitStatus = status;
914         for(int i=0;i<fds.length;i++) if(fds[i] != null) sys_close(i);
915         _exit();
916         return 0;
917     }
918        
919     private int sys_fcntl(int fdn, int cmd, int arg) {
920         final int F_DUPFD = 0;
921         final int F_GETFL = 3;
922         int i;
923             
924         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
925         if(fds[fdn] == null) return -EBADFD;
926         FD fd = fds[fdn];
927         
928         switch(cmd) {
929             case F_DUPFD:
930                 if(arg < 0 || arg >= OPEN_MAX) return -EINVAL;
931                 for(i=arg;i<OPEN_MAX;i++) if(fds[i]==null) break;
932                 if(i==OPEN_MAX) return -EMFILE;
933                 fds[i] = fd.dup();
934                 return 0;
935             case F_GETFL:
936                 int flags = 0;
937                 if(fd.writable() && fd.readable())  flags = 2;
938                 else if(fd.writable()) flags = 1;
939                 return flags;
940             default:
941                 System.err.println("WARNING: Unknown fcntl command: " + cmd);
942                 return -ENOSYS;
943         }
944     }
945             
946     /** The syscall dispatcher.
947         The should be called by subclasses when the syscall instruction is invoked.
948         <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 
949         the contenst of A0, A1, A2, and A3. The call MAY change the state
950         @see Runtime#state state */
951     protected int syscall(int syscall, int a, int b, int c, int d) {
952         switch(syscall) {
953             case SYS_null: return 0;
954             case SYS_exit: return sys_exit(a);
955             case SYS_pause: return sys_pause();
956             case SYS_write: return sys_write(a,b,c);
957             case SYS_fstat: return sys_fstat(a,b);
958             case SYS_sbrk: return sbrk(a);
959             case SYS_open: return sys_open(a,b,c);
960             case SYS_close: return sys_close(a);
961             case SYS_read: return sys_read(a,b,c);
962             case SYS_lseek: return sys_lseek(a,b,c);
963             case SYS_getpid: return sys_getpid();
964             case SYS_calljava: return sys_calljava(a,b,c,d);
965             case SYS_gettimeofday: return sys_gettimeofday(a,b);
966             case SYS_sleep: return sys_sleep(a);
967             case SYS_times: return sys_times(a);
968             case SYS_getpagesize: return sys_getpagesize();
969             case SYS_isatty: return sys_isatty(a);
970             case SYS_fcntl: return sys_fcntl(a,b,c);
971             case SYS_sysconf: return sys_sysconf(a);
972
973             case SYS_kill:
974             case SYS_fork:
975             case SYS_pipe:
976             case SYS_dup2:
977             case SYS_waitpid:
978             case SYS_stat:
979             case SYS_mkdir:
980             case SYS_getcwd:
981             case SYS_chdir:
982                 System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")");
983                 return -ENOSYS;
984             default:
985                 System.err.println("Attempted to use unknown syscall: " + syscall);
986                 return -ENOSYS;
987         }
988     }
989     
990     public int xmalloc(int size) { int p=malloc(size); if(p==0) throw new RuntimeException("malloc() failed"); return p; }
991     public int xrealloc(int addr,int newsize) { int p=realloc(addr,newsize); if(p==0) throw new RuntimeException("realloc() failed"); return p; }
992     public int realloc(int addr, int newsize) { try { return call("realloc",addr,newsize); } catch(CallException e) { return 0; } }
993     public int malloc(int size) { try { return call("malloc",size); } catch(CallException e) { return 0; } }
994     public void free(int p) { try { if(p!=0) call("free",p); } catch(CallException e) { /*noop*/ } }
995     
996     /** Helper function to create a cstring in main memory */
997     public int strdup(String s) {
998         byte[] a;
999         if(s == null) s = "(null)";
1000         byte[] a2 = getBytes(s);
1001         a = new byte[a2.length+1];
1002         System.arraycopy(a2,0,a,0,a2.length);
1003         int addr = malloc(a.length);
1004         if(addr == 0) return 0;
1005         try {
1006             copyout(a,addr,a.length);
1007         } catch(FaultException e) {
1008             free(addr);
1009             return 0;
1010         }
1011         return addr;
1012     }
1013     
1014     /** Helper function to read a cstring from main memory */
1015     public String cstring(int addr) throws ReadFaultException {
1016         StringBuffer sb = new StringBuffer();
1017         for(;;) {
1018             int word = memRead(addr&~3);
1019             switch(addr&3) {
1020                 case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++;
1021                 case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++;
1022                 case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++;
1023                 case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++;
1024             }
1025         }
1026     }
1027     
1028     /** File Descriptor class */
1029     public static abstract class FD {
1030         private int refCount = 1;
1031     
1032         /** returns true if the fd is readable */
1033         public boolean readable() { return false; }
1034         /** returns true if the fd is writable */
1035         public boolean writable() { return false; }
1036         
1037         /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */
1038         public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1039         /** Write. Should return the number of bytes written or throw an IOException on error */
1040         public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1041
1042         /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */
1043         public int seek(int n, int whence)  throws IOException  { return -1; }
1044         
1045         /** Should return true if this is a tty */
1046         public boolean isatty() { return false; }
1047         
1048         private FStat cachedFStat = null;
1049         public final FStat fstat() {
1050             if(cachedFStat == null) cachedFStat = _fstat(); 
1051             return cachedFStat;
1052         }
1053         
1054         protected abstract FStat _fstat();
1055         
1056         /** Closes the fd */
1057         public final void close() { if(--refCount==0) _close(); }
1058         protected void _close() { /* noop*/ }
1059         
1060         FD dup() { refCount++; return this; }
1061     }
1062         
1063     /** FileDescriptor class for normal files */
1064     public abstract static class SeekableFD extends FD {
1065         private final int flags;
1066         private final Seekable data;
1067         public boolean readable() { return (flags&3) != WR_ONLY; }
1068         public boolean writable() { return (flags&3) != RD_ONLY; }
1069         
1070         SeekableFD(Seekable data, int flags) { this.data = data; this.flags = flags; }
1071         
1072         protected abstract FStat _fstat();
1073
1074         public int seek(int n, int whence) throws IOException {
1075             switch(whence) {
1076                 case SEEK_SET: break;
1077                 case SEEK_CUR: n += data.pos(); break;
1078                 case SEEK_END: n += data.length(); break;
1079                 default: return -1;
1080             }
1081             data.seek(n);
1082             return n;
1083         }
1084         
1085         public int write(byte[] a, int off, int length) throws IOException {
1086             // NOTE: There is race condition here but we can't fix it in pure java
1087             if((flags&O_APPEND) != 0) seek(0,SEEK_END);
1088             return data.write(a,off,length);
1089         }
1090         
1091         public int read(byte[] a, int off, int length) throws IOException {
1092             int n = data.read(a,off,length);
1093             return n < 0 ? 0 : n;
1094         }
1095         
1096         protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } }        
1097     }
1098     
1099     public static class OutputStreamFD extends FD {
1100         private OutputStream os;
1101         public boolean writable() { return true; }
1102         public OutputStreamFD(OutputStream os) { this.os = os; }
1103         public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; }
1104         public void _close() { try { os.close(); } catch(IOException e) { /*ignore*/ }  }
1105         public FStat _fstat() { return new FStat(); }
1106     }
1107     
1108     public static class InputStreamFD extends FD {
1109         private InputStream is;
1110         public boolean readable() { return true; }
1111         public InputStreamFD(InputStream is) { this.is = is; }
1112         public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
1113         public void _close() { try { is.close(); } catch(IOException e) { /*ignore*/ } }
1114         public FStat _fstat() { return new FStat(); }
1115     }
1116     
1117     protected static class StdinFD extends InputStreamFD {
1118         public StdinFD(InputStream is) { super(is); }
1119         public void _close() { /* noop */ }
1120         public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1121         public boolean isatty() { return true; }
1122     }
1123     protected static class StdoutFD extends OutputStreamFD {
1124         public StdoutFD(OutputStream os) { super(os); }
1125         public void _close() { /* noop */ }
1126         public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1127         public boolean isatty() { return true; }
1128     }
1129     
1130     public static class FStat {
1131         public static final int S_IFIFO = 0010000;
1132         public static final int S_IFCHR = 0020000;
1133         public static final int S_IFDIR = 0040000;
1134         public static final int S_IFREG = 0100000;
1135         
1136         public int dev() { return -1; }
1137         // FEATURE: inode numbers are calculated inconsistently throught the runtime
1138         public int inode() { return hashCode() & 0xfffff; }
1139         public int mode() { return 0; }
1140         public int type() { return S_IFIFO; }
1141         public int nlink() { return 0; }
1142         public int uid() { return 0; }
1143         public int gid() { return 0; }
1144         public int size() { return 0; }
1145         public int atime() { return 0; }
1146         public int mtime() { return 0; }
1147         public int ctime() { return 0; }
1148         public int blksize() { return 512; }
1149         public int blocks() { return (size()+blksize()-1)/blksize(); }        
1150     }
1151     
1152     protected static class HostFStat extends FStat {
1153         private final File f;
1154         private final boolean executable; 
1155         public HostFStat(File f) {
1156             this.f = f;
1157             String name = f.getName();
1158             // FEATURE: This is ugly.. maybe we should do a file(1) type check
1159             executable = name.endsWith(".mips") || name.endsWith(".sh");
1160         }
1161         public int dev() { return 1; }
1162         public int inode() { return f.getName().hashCode() & 0xffff; }
1163         public int type() { return f.isDirectory() ? S_IFDIR : S_IFREG; }
1164         public int nlink() { return 1; }
1165         public int mode() {
1166             int mode = 0;
1167             boolean canread = f.canRead();
1168             if(canread && (executable || f.isDirectory())) mode |= 0111;
1169             if(canread) mode |= 0444;
1170             if(f.canWrite()) mode |= 0222;
1171             return mode;
1172         }
1173         public int size() { return (int) f.length(); }
1174         public int mtime() { return (int)(f.lastModified()/1000); }
1175     }
1176     
1177     // Exceptions
1178     public class ReadFaultException extends FaultException {
1179         public ReadFaultException(int addr) { super(addr); }
1180     }
1181     public class WriteFaultException extends FaultException {
1182         public WriteFaultException(int addr) { super(addr); }
1183     }
1184     public abstract class FaultException extends ExecutionException {
1185         public int addr;
1186         public FaultException(int addr) { super("fault at: " + toHex(addr)); this.addr = addr; }
1187     }
1188     public static class ExecutionException extends Exception {
1189         private String message = "(null)";
1190         private String location = "(unknown)";
1191         public ExecutionException() { /* noop */ }
1192         public ExecutionException(String s) { if(s != null) message = s; }
1193         void setLocation(String s) { location = s == null ? "(unknown)" : s; }
1194         public final String getMessage() { return message + " at " + location; }
1195     }
1196     public static class CallException extends Exception {
1197         public CallException(String s) { super(s); }
1198     }
1199     
1200     protected static class ErrnoException extends IOException {
1201         public int errno;
1202         public ErrnoException(int errno) { super("Errno: " + errno); this.errno = errno; }
1203     }
1204     
1205     // CPU State
1206     protected static class CPUState {
1207         public CPUState() { /* noop */ }
1208         /* GPRs */
1209         public int[] r = new int[32];
1210         /* Floating point regs */
1211         public int[] f = new int[32];
1212         public int hi, lo;
1213         public int fcsr;
1214         public int pc;
1215     }
1216     
1217     // Null pointer check helper function
1218     protected final void nullPointerCheck(int addr) throws ExecutionException {
1219         if(TOTAL_PAGES==1 ? addr < 65536 : (addr>>>PAGE_SHIFT) < 16)
1220             throw new ExecutionException("Attempted to dereference a null pointer " + toHex(addr));
1221     }
1222     
1223     // Utility functions
1224     private byte[] byteBuf(int size) {
1225         if(_byteBuf==null) _byteBuf = new byte[size];
1226         else if(_byteBuf.length < size)
1227             _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)];
1228         return _byteBuf;
1229     }
1230     
1231     protected static String getSystemProperty(String key) {
1232         try {
1233             return System.getProperty(key);
1234         } catch(SecurityException e) {
1235             return null;
1236         }
1237     }
1238     
1239     /** Decode an packed string.. FEATURE: document this better */
1240     protected static final int[] decodeData(String s, int words) {
1241         if(s.length() % 8 != 0) throw new IllegalArgumentException("string length must be a multiple of 8");
1242         if((s.length() / 8) * 7 < words*4) throw new IllegalArgumentException("string isn't big enough");
1243         int[] buf = new int[words];
1244         int prev = 0, left=0;
1245         for(int i=0,n=0;n<words;i+=8) {
1246             long l = 0;
1247             for(int j=0;j<8;j++) { l <<= 7; l |= s.charAt(i+j) & 0x7f; }
1248             if(left > 0) buf[n++] = prev | (int)(l>>>(56-left));
1249             if(n < words) buf[n++] = (int) (l >>> (24-left));
1250             left = (left + 8) & 0x1f;
1251             prev = (int)(l << left);
1252         }
1253         return buf;
1254     }
1255     
1256     protected static byte[] getBytes(String s) {
1257         try {
1258             return s.getBytes("ISO-8859-1");
1259         } catch(UnsupportedEncodingException e) {
1260             return null; // should never happen
1261         }
1262     }
1263     
1264     protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
1265     protected final static int min(int a, int b) { return a < b ? a : b; }
1266     protected final static int max(int a, int b) { return a > b ? a : b; }
1267 }