902e377ce2365a27675fc1c18330caa7bbf028f1
[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     /** Pointer to a callback for the call_java syscall */
91     protected CallJavaCB callJavaCB;
92     public void setCallJavaCB(CallJavaCB callJavaCB) { this.callJavaCB = callJavaCB; }
93     public CallJavaCB getCallJavaCB() { return callJavaCB; }
94         
95     /** Temporary buffer for read/write operations */
96     private byte[] _byteBuf = null;
97     /** Max size of temporary buffer
98         @see Runtime#_byteBuf */
99     private final static int MAX_CHUNK = 15*1024*1024;
100         
101     /** Subclasses should actually execute program in this method. They should continue 
102         executing until state != RUNNING. Only syscall() can modify state. It is safe 
103         to only check the state attribute after a call to syscall() */
104     protected abstract void _execute() throws ExecutionException;
105     
106     /** Subclasses should return the address of the symbol <i>symbol</i> or -1 it it doesn't exits in this method 
107         This method is only required if the call() function is used */
108     protected int lookupSymbol(String symbol) { return -1; }
109     
110     /** Subclasses should returns a CPUState object representing the cpu state */
111     protected abstract CPUState getCPUState();
112     
113     /** Subclasses should set the CPUState to the state held in <i>state</i> */
114     protected abstract void setCPUState(CPUState state);
115
116     static void checkPageSize(int pageSize, int totalPages) throws IllegalArgumentException {
117         if(pageSize < 256) throw new IllegalArgumentException("pageSize too small");
118         if((pageSize&(pageSize-1)) != 0) throw new IllegalArgumentException("pageSize must be a power of two");
119         if((totalPages&(totalPages-1)) != 0) throw new IllegalArgumentException("totalPages must be a power of two");
120         if(totalPages != 1 && totalPages < 256) throw new IllegalArgumentException("totalPages too small");
121         if(totalPages * pageSize < 4*1024*1024) throw new IllegalArgumentException("total memory too small (" + totalPages + "*" + pageSize + ")");
122     }
123     
124     protected Runtime(int pageSize, int totalPages, boolean allowEmptyPages) {
125         this.allowEmptyPages = allowEmptyPages;
126         
127         checkPageSize(pageSize,totalPages);
128         
129         PAGE_SIZE = pageSize;
130         PAGE_WORDS = pageSize>>>2;
131         int pageShift = 0;
132         while(pageSize>>>pageShift != 1) pageShift++;
133         PAGE_SHIFT = pageShift;
134         
135         TOTAL_PAGES = totalPages;
136         
137         readPages = new int[TOTAL_PAGES][];
138         writePages = new int[TOTAL_PAGES][];
139         
140         if(TOTAL_PAGES == 1) {
141             readPages[0] = writePages[0] = new int[PAGE_WORDS];
142             BRK_LIMIT = STACK_BOTTOM = 0;
143         } else {
144             int stackPages = max(TOTAL_PAGES>>>8,(1024*1024)>>>PAGE_SHIFT);
145             STACK_BOTTOM = (TOTAL_PAGES - stackPages) * PAGE_SIZE;
146             // leave some unmapped pages between the stack and the heap
147             BRK_LIMIT = STACK_BOTTOM - 4*PAGE_SIZE;
148         
149             for(int i=0;i<stackPages;i++)
150                 readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage();
151         }
152         
153         addFD(new StdinFD(System.in));
154         addFD(new StdoutFD(System.out));
155         addFD(new StdoutFD(System.err));
156     }
157     
158     /** Copy everything from <i>src</i> to <i>addr</i> initializing uninitialized pages if required. 
159        Newly initalized pages will be marked read-only if <i>ro</i> is set */
160     protected final void initPages(int[] src, int addr, boolean ro) {
161         for(int i=0;i<src.length;) {
162             int page = addr >>> PAGE_SHIFT;
163             int start = (addr&(PAGE_SIZE-1))>>2;
164             int elements = min(PAGE_WORDS-start,src.length-i);
165             if(readPages[page]==null) {
166                 initPage(page,ro);
167             } else if(!ro) {
168                 if(writePages[page] == null) writePages[page] = readPages[page];
169             }
170             System.arraycopy(src,i,readPages[page],start,elements);
171             i += elements;
172             addr += elements*4;
173         }
174     }
175     
176     /** Initialize <i>words</i> of pages starting at <i>addr</i> to 0 */
177     protected final void clearPages(int addr, int words) {
178         for(int i=0;i<words;) {
179             int page = addr >>> PAGE_SHIFT;
180             int start = (addr&(PAGE_SIZE-1))>>2;
181             int elements = min(PAGE_WORDS-start,words-i);
182             if(readPages[page]==null) {
183                 readPages[page] = writePages[page] = emptyPage();
184             } else {
185                 if(writePages[page] == null) writePages[page] = readPages[page];
186                 for(int j=start;j<start+elements;j++) writePages[page][j] = 0;
187             }
188             i += elements;
189             addr += elements*4;
190         }
191     }
192     
193     /** Copies <i>length</i> bytes from the processes memory space starting at
194         <i>addr</i> INTO a java byte array <i>a</i> */
195     public final void copyin(int addr, byte[] buf, int count) throws ReadFaultException {
196         int x=0;
197         if((addr&3)!=0) {
198             int word = memRead(addr&~3);
199             switch(addr&3) {
200                 case 1: buf[x++] = (byte)((word>>>16)&0xff); if(--count==0) break;
201                 case 2: buf[x++] = (byte)((word>>> 8)&0xff); if(--count==0) break;
202                 case 3: buf[x++] = (byte)((word>>> 0)&0xff); if(--count==0) break;
203             }
204             addr = (addr&~3)+4;
205         }
206         if((count&~3) != 0) {
207             int c = count>>>2;
208             int a = addr>>>2;
209             while(c != 0) {
210                 int[] page = readPages[a >>> (PAGE_SHIFT-2)];
211                 if(page == null) throw new ReadFaultException(a<<2);
212                 int index = a&(PAGE_WORDS-1);
213                 int n = min(c,PAGE_WORDS-index);
214                 if(page != _emptyPage) {
215                     for(int i=0;i<n;i++,x+=4) {
216                         int word = page[index+i];
217                         buf[x+0] = (byte)((word>>>24)&0xff); buf[x+1] = (byte)((word>>>16)&0xff);
218                         buf[x+2] = (byte)((word>>> 8)&0xff); buf[x+3] = (byte)((word>>> 0)&0xff);                        
219                     }
220                 }
221                 a += n; c -=n;
222             }
223             addr = a<<2; count &=3;
224         }
225         if(count != 0) {
226             int word = memRead(addr);
227             switch(count) {
228                 case 3: buf[x+2] = (byte)((word>>>8)&0xff);
229                 case 2: buf[x+1] = (byte)((word>>>16)&0xff);
230                 case 1: buf[x+0] = (byte)((word>>>24)&0xff);
231             }
232         }
233     }
234     
235     /** Copies <i>length</i> bytes OUT OF the java array <i>a</i> into the processes memory
236         space at <i>addr</i> */
237     public final void copyout(byte[] buf, int addr, int count) throws FaultException {
238         int x=0;
239         if((addr&3)!=0) {
240             int word = memRead(addr&~3);
241             switch(addr&3) {
242                 case 1: word = (word&0xff00ffff)|((buf[x++]&0xff)<<16); if(--count==0) break;
243                 case 2: word = (word&0xffff00ff)|((buf[x++]&0xff)<< 8); if(--count==0) break;
244                 case 3: word = (word&0xffffff00)|((buf[x++]&0xff)<< 0); if(--count==0) break;
245             }
246             memWrite(addr&~3,word);
247             addr += x;
248         }
249         if((count&~3) != 0) {
250             int c = count>>>2;
251             int a = addr>>>2;
252             while(c != 0) {
253                 int[] page = writePages[a >>> (PAGE_SHIFT-2)];
254                 if(page == null) throw new WriteFaultException(a<<2);
255                 if(page == _emptyPage) page = initPage(a >>> (PAGE_SHIFT-2));
256                 int index = a&(PAGE_WORDS-1);
257                 int n = min(c,PAGE_WORDS-index);
258                 for(int i=0;i<n;i++,x+=4)
259                     page[index+i] = ((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8)|((buf[x+3]&0xff)<<0);
260                 a += n; c -=n;
261             }
262             addr = a<<2; count&=3;
263         }
264         if(count != 0) {
265             int word = memRead(addr);
266             switch(count) {
267                 case 1: word = (word&0x00ffffff)|((buf[x+0]&0xff)<<24); break;
268                 case 2: word = (word&0x0000ffff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16); break;
269                 case 3: word = (word&0x000000ff)|((buf[x+0]&0xff)<<24)|((buf[x+1]&0xff)<<16)|((buf[x+2]&0xff)<<8); break;
270             }
271             memWrite(addr,word);
272         }
273     }
274     
275     public final void memcpy(int dst, int src, int count) throws FaultException {
276         if((dst&3) == 0 && (src&3)==0) {
277             if((count&~3) != 0) {
278                 int c = count>>2;
279                 int s = src>>>2;
280                 int d = dst>>>2;
281                 while(c != 0) {
282                     int[] srcPage = readPages[s>>>(PAGE_SHIFT-2)];
283                     if(srcPage == null) throw new ReadFaultException(s<<2);
284                     int[] dstPage = writePages[d>>>(PAGE_SHIFT-2)];
285                     if(dstPage == null) throw new WriteFaultException(d<<2);
286                     int srcIndex = (s&(PAGE_WORDS-1));
287                     int dstIndex = (d&(PAGE_WORDS-1));
288                     int n = min(c,PAGE_WORDS-max(srcIndex,dstIndex));
289                     if(srcPage != _emptyPage) {
290                         if(dstPage == _emptyPage) dstPage = initPage(d>>>(PAGE_SHIFT-2));
291                         System.arraycopy(srcPage,srcIndex,dstPage,dstIndex,n);
292                     } else if(srcPage == _emptyPage && dstPage != _emptyPage) {
293                         Arrays.fill(dstPage,dstIndex,dstIndex+n,0);
294                     }
295                     s += n; d += n; c -= n;
296                 }
297                 src = s<<2; dst = d<<2; count&=3;
298             }
299             if(count != 0) {
300                 int word1 = memRead(src);
301                 int word2 = memRead(dst);
302                 switch(count) {
303                     case 1: memWrite(dst,(word1&0xff000000)|(word2&0x00ffffff)); break;
304                     case 2: memWrite(dst,(word1&0xffff0000)|(word2&0x0000ffff)); break;
305                     case 3: memWrite(dst,(word1&0xffffff00)|(word2&0x000000ff)); break;
306                 }
307             }
308         } else {
309             while(count > 0) {
310                 int n = min(count,MAX_CHUNK);
311                 byte[] buf = byteBuf(n);
312                 copyin(src,buf,n);
313                 copyout(buf,dst,n);
314                 count -= n; src += n; dst += n;
315             }
316         }
317     }
318     
319     public final void memset(int addr, int ch, int count) throws FaultException {
320         int fourBytes = ((ch&0xff)<<24)|((ch&0xff)<<16)|((ch&0xff)<<8)|((ch&0xff)<<0);
321         if((addr&3)!=0) {
322             int word = memRead(addr&~3);
323             switch(addr&3) {
324                 case 1: word = (word&0xff00ffff)|((ch&0xff)<<16); if(--count==0) break;
325                 case 2: word = (word&0xffff00ff)|((ch&0xff)<< 8); if(--count==0) break;
326                 case 3: word = (word&0xffffff00)|((ch&0xff)<< 0); if(--count==0) break;
327             }
328             memWrite(addr&~3,word);
329             addr = (addr&~3)+4;
330         }
331         if((count&~3) != 0) {
332             int c = count>>2;
333             int a = addr>>>2;
334             while(c != 0) {
335                 int[] page = readPages[a>>>(PAGE_SHIFT-2)];
336                 if(page == null) throw new WriteFaultException(a<<2);
337                 int index = (a&(PAGE_WORDS-1));
338                 int n = min(c,PAGE_WORDS-index);
339                 if(page != _emptyPage || ch != 0) {
340                     if(page == _emptyPage) page = initPage(a>>>(PAGE_SHIFT-2));
341                     Arrays.fill(page,index,index+n,fourBytes);
342                 }
343                 a += n; c -= n;
344             }
345             addr = a<<2; count&=3;
346         }
347         if(count != 0) {
348             int word = memRead(addr);
349             switch(count) {
350                 case 1: word = (word&0x00ffffff)|(fourBytes&0xff000000); break;
351                 case 2: word = (word&0x0000ffff)|(fourBytes&0xffff0000); break;
352                 case 3: word = (word&0x000000ff)|(fourBytes&0xffffff00); break;
353             }
354             memWrite(addr,word);
355         }
356     }
357     
358     /** Read a word from the processes memory at <i>addr</i> */
359     public final int memRead(int addr) throws ReadFaultException  {
360         if((addr & 3) != 0) throw new ReadFaultException(addr);
361         return unsafeMemRead(addr);
362     }
363        
364     protected final int unsafeMemRead(int addr) throws ReadFaultException {
365         int page = addr >>> PAGE_SHIFT;
366         int entry = (addr >>> 2) & (PAGE_WORDS-1);
367         try {
368             return readPages[page][entry];
369         } catch(ArrayIndexOutOfBoundsException e) {
370             if(page < 0) throw e; // should never happen
371             if(page >= readPages.length) throw new ReadFaultException(addr);
372             if(readPages[page] != _emptyPage) throw e; // should never happen
373             initPage(page);
374             return 0;
375         } catch(NullPointerException e) {
376             throw new ReadFaultException(addr);
377         }
378     }
379     
380     /** Writes a word to the processes memory at <i>addr</i> */
381     public final void memWrite(int addr, int value) throws WriteFaultException  {
382         if((addr & 3) != 0) throw new WriteFaultException(addr);
383         unsafeMemWrite(addr,value);
384     }
385     
386     protected final void unsafeMemWrite(int addr, int value) throws WriteFaultException {
387         int page = addr >>> PAGE_SHIFT;
388         int entry = (addr>>>2)&(PAGE_WORDS-1);
389         try {
390             writePages[page][entry] = value;
391         } catch(ArrayIndexOutOfBoundsException e) {
392             if(page < 0) throw e;// should never happen
393             if(page >= writePages.length) throw new WriteFaultException(addr);
394             if(readPages[page] != _emptyPage) throw e; // should never happen
395             initPage(page);
396             writePages[page][entry] = value;
397         } catch(NullPointerException e) {
398             throw new WriteFaultException(addr);
399         }
400     }
401     
402     /** Created a new non-empty writable page at page number <i>page</i> */
403     private final int[] initPage(int page) { return initPage(page,false); }
404     /** Created a new non-empty page at page number <i>page</i>. If <i>ro</i> is set the page will be read-only */
405     private final int[] initPage(int page, boolean ro) {
406         int[] buf = new int[PAGE_WORDS];
407         writePages[page] = ro ? null : buf;
408         readPages[page] = buf;
409         return buf;
410     }
411     
412     /** Returns the exit status of the process. (only valid if state == DONE) 
413         @see Runtime#state */
414     public final int exitStatus() {
415         if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state");
416         return exitStatus;
417     }
418         
419     private int addStringArray(String[] strings, int topAddr) {
420         int count = strings.length;
421         int total = 0; /* null last table entry  */
422         for(int i=0;i<count;i++) total += strings[i].length() + 1;
423         if(total >= ARGS_MAX) throw new IllegalArgumentException("arguments/environ too big");
424         total += (count+1)*4;
425         int start = (topAddr - total)&~3;
426         int addr = start + (count+1)*4;
427         int[] table = new int[count+1];
428         try {
429             for(int i=0;i<count;i++) {
430                 byte[] a = getBytes(strings[i]);
431                 table[i] = addr;
432                 copyout(a,addr,a.length);
433                 memset(addr+a.length,0,1);
434                 addr += a.length + 1;
435             }
436             addr=start;
437             for(int i=0;i<count+1;i++) {
438                 memWrite(addr,table[i]);
439                 addr += 4;
440             }
441         } catch(FaultException e) {
442             // should never happen
443             throw new Error(e.toString());
444         }
445         return start;
446     }
447     
448     protected String[] createEnv(String[] extra) { if(extra == null) extra = new String[0]; return extra; }
449     
450     /** Sets word number <i>index</i> in the _user_info table to <i>word</i>
451      * The user_info table is a chunk of memory in the program's memory defined by the
452      * symbol "user_info". The compiler/interpreter automatically determine the size
453      * and location of the user_info table from the ELF symbol table. setUserInfo and
454      * getUserInfo are used to modify the words in the user_info table. */
455     public void setUserInfo(int index, int word) {
456         if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
457         try {
458             memWrite(userInfoBase+index*4,word);
459         } catch(FaultException e) { throw new Error("should never happen: " + e); }
460     }
461     
462     /** Returns the word in the _user_info table entry <i>index</i>
463         @see Runtime#setUserInfo(int,int) setUserInfo */
464     public int getUserInfo(int index) {
465         if(index < 0 || index >= userInfoSize/4) throw new IndexOutOfBoundsException("setUserInfo called with index >= " + (userInfoSize/4));
466         try {
467             return memRead(userInfoBase+index*4);
468         } catch(FaultException e) { throw new Error("should never happen: " + e); }
469     }
470     
471     /** Calls _execute() (subclass's execute()) and catches exceptions */
472     private void __execute() {
473         try {
474             _execute();
475         } catch(FaultException e) {
476             e.printStackTrace();
477             sys_exit(128+11); // SIGSEGV
478             exitException = e;
479         } catch(ExecutionException e) {
480             e.printStackTrace();
481             System.err.println(e);
482             sys_exit(128+4); // SIGILL
483             exitException = e;
484         }
485     }
486     
487     /** Executes the process until the PAUSE syscall is invoked or the process exits. Returns true if the process exited. */
488     public final boolean execute()  {
489         if(state != PAUSED) throw new IllegalStateException("execute() called in inappropriate state");
490         if(startTime == 0) startTime = System.currentTimeMillis();
491         state = RUNNING;
492         __execute();
493         if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state (" + state + ")");
494         return state == DONE;
495     }
496     
497     public final int run() { return run(null); }
498     public final int run(String argv0, String[] rest) {
499         String[] args = new String[rest.length+1];
500         System.arraycopy(rest,0,args,1,rest.length);
501         args[0] = argv0;
502         return run(args);
503     }
504     public final int run(String[] args) { return run(args,null); }
505     
506     /** Runs the process until it exits and returns the exit status.
507         If the process executes the PAUSE syscall execution will be paused for 500ms and a warning will be displayed */
508     public final int run(String[] args, String[] env) {
509         start(args,env);
510         for(;;) {
511             if(execute()) break;
512             System.err.println("WARNING: Pause requested while executing run()");
513             try { Thread.sleep(500); } catch(InterruptedException e) { /* noop */ }
514         }
515         return exitStatus();
516     }
517
518     public final void start() { start(null); }
519     public final void start(String[] args) { start(args,null); }
520     
521     /** Initializes the process and prepairs it to be executed with execute() */
522     public final void start(String[] args, String[] environ)  {
523         int sp, argsAddr, envAddr;
524         if(state != INITIALIZED) throw new IllegalStateException("start() called in inappropriate state");
525
526         if(args == null) args = new String[]{getClass().getName()};
527         
528         sp = TOTAL_PAGES*PAGE_SIZE-512;
529         sp = argsAddr = addStringArray(args,sp);
530         sp = envAddr = addStringArray(createEnv(environ),sp);
531         sp &= ~15;
532         
533         CPUState cpuState = new CPUState();
534         cpuState.r[A0] = argsAddr;
535         cpuState.r[A1] = envAddr;
536         cpuState.r[SP] = sp;
537         cpuState.r[RA] = 0xdeadbeef;
538         cpuState.r[GP] = gp;
539         cpuState.pc = entryPoint;
540         setCPUState(cpuState);
541                 
542         state = PAUSED;
543         
544         _start();
545     }
546     
547     /** Hook for subclasses to do their own startup */
548     protected void _start() { /* noop */ }
549     
550     // FEATURE: call() that accepts an Object[] array and automatically allocates strings/arrays/etc on the stack
551     public final int call(String sym) throws CallException { return call(sym,0,0,0,0,0,0,0); }
552     public final int call(String sym, int a0) throws CallException  { return call(sym,a0,0,0,0,0,0,0); }
553     public final int call(String sym, int a0, int a1) throws CallException  { return call(sym,a0,a1,0,0,0,0,0); }
554     public final int call(String sym, int a0, int a1, int a2) throws CallException  { return call(sym,a0,a1,a2,0,0,0,0); }
555     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); }
556     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); }
557     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); }
558     
559     /** Calls a function in the process with the given arguments */
560     public final int call(String sym, int a0, int a1, int a2, int a3, int a4, int a5, int a6) throws CallException {
561         int func = lookupSymbol(sym);
562         if(func == -1) throw new CallException(sym + " not found");
563         int helper = lookupSymbol("_call_helper");
564         if(helper == -1) throw new CallException("_call_helper not found");
565         return call(helper,func,a0,a1,a2,a3,a4,a5,a6);
566     }
567     
568     /** Executes the code at <i>addr</i> in the process setting A0-A3 and S0-S3 to the given arguments
569         and returns the contents of V1 when the the pause syscall is invoked */
570     public final int call(int addr, int a0, int a1, int a2, int a3, int s0, int s1, int s2, int s3) {
571         if(state != PAUSED && state != CALLJAVA) throw new IllegalStateException("call() called in inappropriate state");
572         int oldState = state;
573         CPUState saved = getCPUState();
574         CPUState cpustate = new CPUState();
575         cpustate.r[SP] = saved.r[SP]&~15;
576         cpustate.r[RA] = 0xdeadbeef;
577         cpustate.r[A0] = a0;
578         cpustate.r[A1] = a1;
579         cpustate.r[A2] = a2;
580         cpustate.r[A3] = a3;
581         cpustate.r[S0] = s0;
582         cpustate.r[S1] = s1;
583         cpustate.r[S2] = s2;
584         cpustate.r[S3] = s3;
585         cpustate.r[GP] = gp;
586         cpustate.pc = addr;
587         
588         state = RUNNING;
589
590         setCPUState(cpustate);
591         __execute();
592         cpustate = getCPUState();
593         setCPUState(saved);
594
595         if(state != PAUSED)
596             System.out.println("WARNING: Process exit()ed while servicing a call() request");
597         else
598             state = oldState;
599         
600         return cpustate.r[V1];
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     public static interface CallJavaCB { public int call(int a, int b, int c, int d); }
889     
890     private int sys_calljava(int a, int b, int c, int d) {
891         if(state != RUNNING) throw new IllegalStateException("wound up calling sys_calljava while not in RUNNING");
892         if(callJavaCB != null) {
893             state = CALLJAVA;
894             int ret = callJavaCB.call(a,b,c,d);
895             state = RUNNING;
896             return ret;
897         } else {
898                         System.err.println("WARNING: calljava syscall invoked without a calljava callback set");
899                         return 0;
900         }
901     }
902         
903     private int sys_pause() {
904         state = PAUSED;
905         return 0;
906     }
907     
908     private int sys_getpagesize() { return TOTAL_PAGES == 1 ? 4096 : PAGE_SIZE; }
909     
910     private int sys_isatty(int fdn) {
911         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
912         if(fds[fdn] == null) return -EBADFD;
913         return fds[fdn].isatty() ? 1 : 0;
914     }
915
916     
917     /** Hook for subclasses to do something when the process exits (MUST set state = DONE) */
918     protected void _exit() { state = DONE; }
919     private int sys_exit(int status) {
920         exitStatus = status;
921         for(int i=0;i<fds.length;i++) if(fds[i] != null) sys_close(i);
922         _exit();
923         return 0;
924     }
925        
926     private int sys_fcntl(int fdn, int cmd, int arg) {
927         final int F_DUPFD = 0;
928         final int F_GETFL = 3;
929         int i;
930             
931         if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
932         if(fds[fdn] == null) return -EBADFD;
933         FD fd = fds[fdn];
934         
935         switch(cmd) {
936             case F_DUPFD:
937                 if(arg < 0 || arg >= OPEN_MAX) return -EINVAL;
938                 for(i=arg;i<OPEN_MAX;i++) if(fds[i]==null) break;
939                 if(i==OPEN_MAX) return -EMFILE;
940                 fds[i] = fd.dup();
941                 return 0;
942             case F_GETFL:
943                 int flags = 0;
944                 if(fd.writable() && fd.readable())  flags = 2;
945                 else if(fd.writable()) flags = 1;
946                 return flags;
947             default:
948                 System.err.println("WARNING: Unknown fcntl command: " + cmd);
949                 return -ENOSYS;
950         }
951     }
952             
953     /** The syscall dispatcher.
954         The should be called by subclasses when the syscall instruction is invoked.
955         <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 
956         the contenst of A0, A1, A2, and A3. The call MAY change the state
957         @see Runtime#state state */
958     protected int syscall(int syscall, int a, int b, int c, int d) {
959         switch(syscall) {
960             case SYS_null: return 0;
961             case SYS_exit: return sys_exit(a);
962             case SYS_pause: return sys_pause();
963             case SYS_write: return sys_write(a,b,c);
964             case SYS_fstat: return sys_fstat(a,b);
965             case SYS_sbrk: return sbrk(a);
966             case SYS_open: return sys_open(a,b,c);
967             case SYS_close: return sys_close(a);
968             case SYS_read: return sys_read(a,b,c);
969             case SYS_lseek: return sys_lseek(a,b,c);
970             case SYS_getpid: return sys_getpid();
971             case SYS_calljava: return sys_calljava(a,b,c,d);
972             case SYS_gettimeofday: return sys_gettimeofday(a,b);
973             case SYS_sleep: return sys_sleep(a);
974             case SYS_times: return sys_times(a);
975             case SYS_getpagesize: return sys_getpagesize();
976             case SYS_isatty: return sys_isatty(a);
977             case SYS_fcntl: return sys_fcntl(a,b,c);
978             case SYS_sysconf: return sys_sysconf(a);
979
980             case SYS_kill:
981             case SYS_fork:
982             case SYS_pipe:
983             case SYS_dup2:
984             case SYS_waitpid:
985             case SYS_stat:
986             case SYS_mkdir:
987             case SYS_getcwd:
988             case SYS_chdir:
989                 System.err.println("Attempted to use a UnixRuntime syscall in Runtime (" + syscall + ")");
990                 return -ENOSYS;
991             default:
992                 System.err.println("Attempted to use unknown syscall: " + syscall);
993                 return -ENOSYS;
994         }
995     }
996     
997     public int xmalloc(int size) { int p=malloc(size); if(p==0) throw new RuntimeException("malloc() failed"); return p; }
998     public int xrealloc(int addr,int newsize) { int p=realloc(addr,newsize); if(p==0) throw new RuntimeException("realloc() failed"); return p; }
999     public int realloc(int addr, int newsize) { try { return call("realloc",addr,newsize); } catch(CallException e) { return 0; } }
1000     public int malloc(int size) { try { return call("malloc",size); } catch(CallException e) { return 0; } }
1001     public void free(int p) { try { if(p!=0) call("free",p); } catch(CallException e) { /*noop*/ } }
1002     
1003     /** Helper function to create a cstring in main memory */
1004     public int strdup(String s) {
1005         byte[] a;
1006         if(s == null) s = "(null)";
1007         byte[] a2 = getBytes(s);
1008         a = new byte[a2.length+1];
1009         System.arraycopy(a2,0,a,0,a2.length);
1010         int addr = malloc(a.length);
1011         if(addr == 0) return 0;
1012         try {
1013             copyout(a,addr,a.length);
1014         } catch(FaultException e) {
1015             free(addr);
1016             return 0;
1017         }
1018         return addr;
1019     }
1020     
1021     /** Helper function to read a cstring from main memory */
1022     public String cstring(int addr) throws ReadFaultException {
1023         StringBuffer sb = new StringBuffer();
1024         for(;;) {
1025             int word = memRead(addr&~3);
1026             switch(addr&3) {
1027                 case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++;
1028                 case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++;
1029                 case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++;
1030                 case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++;
1031             }
1032         }
1033     }
1034     
1035     /** File Descriptor class */
1036     public static abstract class FD {
1037         private int refCount = 1;
1038     
1039         /** returns true if the fd is readable */
1040         public boolean readable() { return false; }
1041         /** returns true if the fd is writable */
1042         public boolean writable() { return false; }
1043         
1044         /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */
1045         public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1046         /** Write. Should return the number of bytes written or throw an IOException on error */
1047         public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
1048
1049         /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */
1050         public int seek(int n, int whence)  throws IOException  { return -1; }
1051         
1052         /** Should return true if this is a tty */
1053         public boolean isatty() { return false; }
1054         
1055         private FStat cachedFStat = null;
1056         public final FStat fstat() {
1057             if(cachedFStat == null) cachedFStat = _fstat(); 
1058             return cachedFStat;
1059         }
1060         
1061         protected abstract FStat _fstat();
1062         
1063         /** Closes the fd */
1064         public final void close() { if(--refCount==0) _close(); }
1065         protected void _close() { /* noop*/ }
1066         
1067         FD dup() { refCount++; return this; }
1068     }
1069         
1070     /** FileDescriptor class for normal files */
1071     public abstract static class SeekableFD extends FD {
1072         private final int flags;
1073         private final Seekable data;
1074         public boolean readable() { return (flags&3) != WR_ONLY; }
1075         public boolean writable() { return (flags&3) != RD_ONLY; }
1076         
1077         SeekableFD(Seekable data, int flags) { this.data = data; this.flags = flags; }
1078         
1079         protected abstract FStat _fstat();
1080
1081         public int seek(int n, int whence) throws IOException {
1082             switch(whence) {
1083                 case SEEK_SET: break;
1084                 case SEEK_CUR: n += data.pos(); break;
1085                 case SEEK_END: n += data.length(); break;
1086                 default: return -1;
1087             }
1088             data.seek(n);
1089             return n;
1090         }
1091         
1092         public int write(byte[] a, int off, int length) throws IOException {
1093             // NOTE: There is race condition here but we can't fix it in pure java
1094             if((flags&O_APPEND) != 0) seek(0,SEEK_END);
1095             return data.write(a,off,length);
1096         }
1097         
1098         public int read(byte[] a, int off, int length) throws IOException {
1099             int n = data.read(a,off,length);
1100             return n < 0 ? 0 : n;
1101         }
1102         
1103         protected void _close() { try { data.close(); } catch(IOException e) { /*ignore*/ } }        
1104     }
1105     
1106     public static class OutputStreamFD extends FD {
1107         private OutputStream os;
1108         public boolean writable() { return true; }
1109         public OutputStreamFD(OutputStream os) { this.os = os; }
1110         public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; }
1111         public void _close() { try { os.close(); } catch(IOException e) { /*ignore*/ }  }
1112         public FStat _fstat() { return new FStat(); }
1113     }
1114     
1115     public static class InputStreamFD extends FD {
1116         private InputStream is;
1117         public boolean readable() { return true; }
1118         public InputStreamFD(InputStream is) { this.is = is; }
1119         public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
1120         public void _close() { try { is.close(); } catch(IOException e) { /*ignore*/ } }
1121         public FStat _fstat() { return new FStat(); }
1122     }
1123     
1124     protected static class StdinFD extends InputStreamFD {
1125         public StdinFD(InputStream is) { super(is); }
1126         public void _close() { /* noop */ }
1127         public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1128         public boolean isatty() { return true; }
1129     }
1130     protected static class StdoutFD extends OutputStreamFD {
1131         public StdoutFD(OutputStream os) { super(os); }
1132         public void _close() { /* noop */ }
1133         public FStat _fstat() { return new FStat() { public int type() { return S_IFCHR; } }; }
1134         public boolean isatty() { return true; }
1135     }
1136     
1137     public static class FStat {
1138         public static final int S_IFIFO = 0010000;
1139         public static final int S_IFCHR = 0020000;
1140         public static final int S_IFDIR = 0040000;
1141         public static final int S_IFREG = 0100000;
1142         
1143         public int dev() { return -1; }
1144         // FEATURE: inode numbers are calculated inconsistently throught the runtime
1145         public int inode() { return hashCode() & 0xfffff; }
1146         public int mode() { return 0; }
1147         public int type() { return S_IFIFO; }
1148         public int nlink() { return 0; }
1149         public int uid() { return 0; }
1150         public int gid() { return 0; }
1151         public int size() { return 0; }
1152         public int atime() { return 0; }
1153         public int mtime() { return 0; }
1154         public int ctime() { return 0; }
1155         public int blksize() { return 512; }
1156         public int blocks() { return (size()+blksize()-1)/blksize(); }        
1157     }
1158     
1159     protected static class HostFStat extends FStat {
1160         private final File f;
1161         private final boolean executable; 
1162         public HostFStat(File f) {
1163             this.f = f;
1164             String name = f.getName();
1165             // FEATURE: This is ugly.. maybe we should do a file(1) type check
1166             executable = name.endsWith(".mips") || name.endsWith(".sh");
1167         }
1168         public int dev() { return 1; }
1169         public int inode() { return f.getName().hashCode() & 0xffff; }
1170         public int type() { return f.isDirectory() ? S_IFDIR : S_IFREG; }
1171         public int nlink() { return 1; }
1172         public int mode() {
1173             int mode = 0;
1174             boolean canread = f.canRead();
1175             if(canread && (executable || f.isDirectory())) mode |= 0111;
1176             if(canread) mode |= 0444;
1177             if(f.canWrite()) mode |= 0222;
1178             return mode;
1179         }
1180         public int size() { return (int) f.length(); }
1181         public int mtime() { return (int)(f.lastModified()/1000); }
1182     }
1183     
1184     // Exceptions
1185     public class ReadFaultException extends FaultException {
1186         public ReadFaultException(int addr) { super(addr); }
1187     }
1188     public class WriteFaultException extends FaultException {
1189         public WriteFaultException(int addr) { super(addr); }
1190     }
1191     public abstract class FaultException extends ExecutionException {
1192         public int addr;
1193         public FaultException(int addr) { super("fault at: " + toHex(addr)); this.addr = addr; }
1194     }
1195     public static class ExecutionException extends Exception {
1196         private String message = "(null)";
1197         private String location = "(unknown)";
1198         public ExecutionException() { /* noop */ }
1199         public ExecutionException(String s) { if(s != null) message = s; }
1200         void setLocation(String s) { location = s == null ? "(unknown)" : s; }
1201         public final String getMessage() { return message + " at " + location; }
1202     }
1203     public static class CallException extends Exception {
1204         public CallException(String s) { super(s); }
1205     }
1206     
1207     protected static class ErrnoException extends IOException {
1208         public int errno;
1209         public ErrnoException(int errno) { super("Errno: " + errno); this.errno = errno; }
1210     }
1211     
1212     // CPU State
1213     protected static class CPUState {
1214         public CPUState() { /* noop */ }
1215         /* GPRs */
1216         public int[] r = new int[32];
1217         /* Floating point regs */
1218         public int[] f = new int[32];
1219         public int hi, lo;
1220         public int fcsr;
1221         public int pc;
1222     }
1223     
1224     // Null pointer check helper function
1225     protected final void nullPointerCheck(int addr) throws ExecutionException {
1226         if(TOTAL_PAGES==1 ? addr < 65536 : (addr>>>PAGE_SHIFT) < 16)
1227             throw new ExecutionException("Attempted to dereference a null pointer " + toHex(addr));
1228     }
1229     
1230     // Utility functions
1231     private byte[] byteBuf(int size) {
1232         if(_byteBuf==null) _byteBuf = new byte[size];
1233         else if(_byteBuf.length < size)
1234             _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)];
1235         return _byteBuf;
1236     }
1237     
1238     protected static String getSystemProperty(String key) {
1239         try {
1240             return System.getProperty(key);
1241         } catch(SecurityException e) {
1242             return null;
1243         }
1244     }
1245     
1246     /** Decode an packed string.. FEATURE: document this better */
1247     protected static final int[] decodeData(String s, int words) {
1248         if(s.length() % 8 != 0) throw new IllegalArgumentException("string length must be a multiple of 8");
1249         if((s.length() / 8) * 7 < words*4) throw new IllegalArgumentException("string isn't big enough");
1250         int[] buf = new int[words];
1251         int prev = 0, left=0;
1252         for(int i=0,n=0;n<words;i+=8) {
1253             long l = 0;
1254             for(int j=0;j<8;j++) { l <<= 7; l |= s.charAt(i+j) & 0x7f; }
1255             if(left > 0) buf[n++] = prev | (int)(l>>>(56-left));
1256             if(n < words) buf[n++] = (int) (l >>> (24-left));
1257             left = (left + 8) & 0x1f;
1258             prev = (int)(l << left);
1259         }
1260         return buf;
1261     }
1262     
1263     protected static byte[] getBytes(String s) {
1264         try {
1265             return s.getBytes("ISO-8859-1");
1266         } catch(UnsupportedEncodingException e) {
1267             return null; // should never happen
1268         }
1269     }
1270     
1271     protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
1272     protected final static int min(int a, int b) { return a < b ? a : b; }
1273     protected final static int max(int a, int b) { return a > b ? a : b; }
1274 }