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