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