89918a76ca5a811345251a780fe163dfbe1102f7
[org.ibex.core.git] / src / org / xwt / mips / Runtime.java
1 // Copyright 2003 Brian Alliet
2 // Based on org.xwt.imp.MIPS by Adam Megacz
3 // Portions Copyright 2003 Adam Megacz
4 package org.xwt.mips;
5 import java.io.*;
6
7 public abstract class Runtime implements Syscalls, Errno,Registers {
8     /** Pages are 4k in size */
9     final static int PAGE_SIZE = 4096;
10     final static int PAGE_WORDS = (int)(PAGE_SIZE >>> 2);
11     final static int PAGE_SHIFT = 12;
12     /** There are 65536 pages available for a total of 256mb of addressable memory */
13     protected final static int TOTAL_PAGES = 65536;
14     /** The top 256 pages are reserved for the stack. Arguments and userdata info use up the first few pages */
15     protected final static int STACK_PAGES = 256;
16     /** This is the upper limit of the pages allocated by the brk() syscall. */
17     protected final static int BRK_LIMIT = TOTAL_PAGES - STACK_PAGES - 1024;
18
19     /* High memory layout
20        TOP
21        <-- ((TOTAL_PAGES-0)*PAGE_SIZE) -->
22        Empty Page
23        <-- ((TOTAL_PAGES-1)*PAGE_SIZE) -->
24        Args (1 page)
25        <-- ((TOTAL_PAGES-2)*PAGE_SIZE) -->
26        User info (1 page)
27        <-- ((TOTAL_PAGES-3)*PAGE_SIZE) -->
28        Empty page
29        <-- ((TOTAL_PAGES-4)*PAGE_SIZE) -->
30        Stack top
31     */
32
33     /** The base address for the args and user_info (this will be passed to crt0.c)
34        The args must be at STUFF_BASE+1 page and user_info must be at STUFF_BASE */
35     protected final static int STUFF_BASE = (TOTAL_PAGES-3)*PAGE_SIZE;
36     protected final static int ARGS_ADDR = STUFF_BASE + PAGE_SIZE;
37     protected final static int USER_INFO_ADDR = STUFF_BASE;
38     
39     /** The initial stack pointer address */
40     protected final static int INITIAL_SP = STUFF_BASE - PAGE_SIZE;
41     
42     /** True if we allow empty pages (_emptyPage) to exist in memory.
43        Empty pages are pages which are allocated by the program but do not contain any
44        data yet (they are all 0s). If empty pages are allowed subclasses must always
45        access main memory with the memRead and memWrite functions */
46     private final boolean allowEmptyPages;
47     /** the "empty page" */
48     private final static int[] _emptyPage = new int[0];
49         
50     /** Returns a new empty page (_emptyPage is empty pages are enabled or a new zero'd page) */
51     private final int[] emptyPage() { return allowEmptyPages ? _emptyPage : new int[PAGE_WORDS]; }
52     
53     /** Readable main memory pages */
54     protected final int[][] readPages;
55     /** Writable main memory pages.
56         If the page is writable writePages[x] == readPages[x]; if not writePages[x] == null. */
57     protected final int[][] writePages;
58     
59     /** The current break between the heap and unallocated memory and the stack.
60         This is the page number NOT an address */
61     protected int brk;
62         
63     /** The program's entry point */
64     protected int entryPoint;
65     
66     /** State contant: There is no program loaded in memory */
67     public final static int UNINITIALIZED = 0; 
68     /**  Text/Data loaded in memory  */
69     public final static int INITIALIZED = 1;
70     /** Program is executing instructions */
71     public final static int RUNNING = 2;
72     /** Prgram has been started but is paused */
73     public final static int PAUSED = 3;
74     /** Program has exited (it cannot currently be restarted) */
75     public final static int DONE = 4;
76     
77     /** The current state (UNINITIALIZED, INITIALIZED, RUNNING, PAUSED, or DONE) */
78     protected int state = UNINITIALIZED;
79     /** @see Runtime#state state */
80     public final int getState() { return state; }
81     
82     /** The exit status if the process (only valid if state==DONE) 
83         @see Runtime#state */
84     protected int exitStatus;
85     
86     /** Maximum number of open file descriptors */
87     private final static int OPEN_MAX = 256;
88     /** Table containing all open file descriptors. (Entries are null if the fd is not in use */
89     private FileDescriptor[] fds;
90     
91     /** Temporary buffer for read/write operations */
92     private byte[] _byteBuf = null;
93     /** Max size of temporary buffer
94         @see Runtime#_byteBuf */
95     private final static int MAX_CHUNK = 4*1024*1024;
96     
97     /** The pid of this "process" */
98     public static final int PID = 1;
99         
100     /** Subclasses should actually execute program in this method. They should continue 
101         executing until state != RUNNING. Only syscall() can modify state. It is safe 
102         to only check the state attribyte after a call to syscall() */
103     protected abstract void _execute() throws ExecutionException;
104     
105     /** This should setup the system to begin executing at pc and 
106        initialize the cpu registers as follows
107        K0 (r26) = STUFF_BASE
108        K1 (r27) = PAGE_SIZE
109        SP (r29) = INITIAL_SP
110        RA (r31) = 0xdeadbeef
111     */
112     protected abstract void _start(int pc);
113     
114     /** Initialize the Runtime with empty pages disabled
115         @see Runtime#Runtime(boolean) */
116     public Runtime() { this(false); }
117     
118     /** Initialize the Runtime. Empty pages are enabled if <i>allowEmptyPages</i> is set */
119     public Runtime(boolean allowEmptyPages) {
120         this.allowEmptyPages = allowEmptyPages;
121         readPages = new int[TOTAL_PAGES][];
122         writePages = new int[TOTAL_PAGES][];
123         for(int i=0;i<STACK_PAGES;i++)
124             readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage();
125     }
126     
127     /** Copy everything from <i>src</i> to <i>addr</i> initializing uninitialized pages if required. 
128        Newly initalized pages will be marked read-only if <i>ro</i> is set */
129     protected final void initPages(int[] src, int addr, boolean ro) {
130         for(int i=0;i<src.length;) {
131             int page = addr >>> PAGE_SHIFT;
132             int start = (addr&(PAGE_SIZE-1))>>2;
133             int elements = min(PAGE_WORDS-start,src.length-i);
134             if(readPages[page]==null) {
135                 initPage(page,ro);
136             } else if(!ro) {
137                 if(writePages[page] == null) writePages[page] = readPages[page];
138             }
139             System.arraycopy(src,i,readPages[page],start,elements);
140             i += elements;
141             addr += elements*4;
142         }
143     }
144     
145     /** Initialize <i>words</i> of pages starting at <i>addr</i> to 0 */
146     protected final void clearPages(int addr, int words) {
147         for(int i=0;i<words;) {
148             int page = addr >>> PAGE_SHIFT;
149             int start = (addr&(PAGE_SIZE-1))>>2;
150             int elements = min(PAGE_WORDS-start,words-i);
151             if(readPages[page]==null) {
152                 readPages[page] = writePages[page] = emptyPage();
153             } else {
154                 if(writePages[page] == null) writePages[page] = readPages[page];
155                 for(int j=start;j<start+elements;j++) writePages[page][j] = 0;
156             }
157             i += elements;
158             addr += elements*4;
159         }
160     }
161     
162     /** Copies <i>length</i> bytes from the processes memory space starting at
163         <i>addr</i> INTO a java byte array <i>a</i> */
164     public final void copyin(int addr, byte[] a, int length) throws ReadFaultException {
165         int n=0;
166         if((addr&3)!=0) {
167             int word = memRead(addr&~3);
168             switch(addr&3) {
169                 case 1: a[n++] = (byte)((word>>>16)&0xff); if(length-n==0) break;
170                 case 2: a[n++] = (byte)((word>>> 8)&0xff); if(length-n==0) break;
171                 case 3: a[n++] = (byte)((word>>> 0)&0xff); if(length-n==0) break;
172             }
173             addr = (addr&~3)+4;
174         }
175         while(length-n > 3) {
176             int start = (addr&(PAGE_SIZE-1))>>2;
177             int end = start + (min(PAGE_SIZE-(addr&(PAGE_SIZE-1)),(length-n)&~3) >> 2);
178             int[] page = readPages[addr >>> PAGE_SHIFT];
179             if(page == null) throw new ReadFaultException(addr);
180             if(page == _emptyPage) { addr+=(end-start)<<2; n+=(end-start)<<2; continue; }
181             for(int i=start;i<end;i++,addr+=4) {
182                 int word = page[i];
183                 a[n++] = (byte)((word>>>24)&0xff); a[n++] = (byte)((word>>>16)&0xff);
184                 a[n++] = (byte)((word>>> 8)&0xff); a[n++] = (byte)((word>>> 0)&0xff);
185             }
186         }
187         if(length-n > 0) {
188             int word = memRead(addr);
189             if(length-n >= 1) a[n+0] = (byte)((word>>>24)&0xff);
190             if(length-n >= 2) a[n+1] = (byte)((word>>>16)&0xff);
191             if(length-n >= 3) a[n+2] = (byte)((word>>> 8)&0xff);
192         }
193     }
194     
195     /** Copies <i>length</i> bytes OUT OF the java array <i>a</i> into the processes memory
196         space at <i>addr</i> */
197     public final void copyout(byte[] a, int addr, int length) throws FaultException {
198         int n=0;
199         if((addr&3)!=0) {
200             int word = memRead(addr&~3);
201             switch(addr&3) {
202                 case 1: word = (word&0xff00ffff)|((a[n]&0xff)<<16); n++; if(length-n==0) break;
203                 case 2: word = (word&0xffff00ff)|((a[n]&0xff)<< 8); n++; if(length-n==0) break;
204                 case 3: word = (word&0xffffff00)|((a[n]&0xff)<< 0); n++; if(length-n==0) break;
205             }
206             memWrite(addr&~3,word);
207             addr = (addr&~3)+4;
208         }
209         
210         while(length-n > 3) {
211             int start = (addr&(PAGE_SIZE-1))>>2;
212             int end = start + (min(PAGE_SIZE-(addr&(PAGE_SIZE-1)),(length-n)&~3) >> 2);
213             int[] page = writePages[addr >>> PAGE_SHIFT];
214             if(page == null) throw new WriteFaultException(addr);
215             if(page == _emptyPage) { memWrite(addr,0); page = writePages[addr >>> PAGE_SHIFT]; }
216             for(int i=start;i<end;i++,addr+=4) {
217                 int word = ((a[n+0]&0xff)<<24)|((a[n+1]&0xff)<<16)|((a[n+2]&0xff)<<8)|((a[n+3]&0xff)<<0); n+=4;
218                 page[i] = word;
219             }
220         }
221         if(length-n > 0) {
222             int word = memRead(addr);
223             if(length-n >= 1) { word = (word&0x00ffffff)|((a[n+0]&0xff)<<24); }
224             if(length-n >= 2) { word = (word&0xff00ffff)|((a[n+1]&0xff)<<16); }
225             if(length-n >= 3) { word = (word&0xffff00ff)|((a[n+2]&0xff)<< 8); }
226             memWrite(addr,word);
227         }
228     }
229     
230     /** Read a word from the processes memory at <i>addr</i> */
231     public final int memRead(int addr) throws ReadFaultException  {
232         if((addr & 3) != 0) throw new ReadFaultException(addr);
233         int page = addr >>> PAGE_SHIFT;
234         int entry = (addr >>> 2) & (PAGE_WORDS-1);
235         try {
236             return readPages[page][entry];
237         } catch(ArrayIndexOutOfBoundsException e) {
238             if(page < 0) throw e; // should never happen
239             if(page > readPages.length) throw new ReadFaultException(addr);
240             if(readPages[page] != _emptyPage) throw e; // should never happen
241             initPage(page);
242             return 0;
243         } catch(NullPointerException e) {
244             throw new ReadFaultException(addr);
245         }
246     }
247     
248     /** Writes a word to the processes memory at <i>addr</i> */
249     public final void memWrite(int addr, int value) throws WriteFaultException  {
250         if((addr & 3) != 0) throw new WriteFaultException(addr);
251         int page = addr >>> PAGE_SHIFT;
252         int entry = (addr>>>2)&(PAGE_WORDS-1);
253         try {
254             writePages[page][entry] = value;
255         } catch(ArrayIndexOutOfBoundsException e) {
256             if(page < 0) throw e;// should never happen
257             if(page > writePages.length) throw new WriteFaultException(addr);
258             if(readPages[page] != _emptyPage) throw e; // should never happen
259             initPage(page);
260             writePages[page][entry] = value;
261         } catch(NullPointerException e) {
262             throw new WriteFaultException(addr);
263         }
264     }
265     
266     /** Created a new non-empty writable page at page number <i>page</i> */
267     private final void initPage(int page) { initPage(page,false); }
268     /** Created a new non-empty page at page number <i>page</i>. If <i>ro</i> is set the page will be read-only */
269     private final void initPage(int page, boolean ro) {
270         int[] buf = new int[PAGE_WORDS];
271         writePages[page] = ro ? null : buf;
272         readPages[page] = buf;
273     }
274     
275     /** Returns the exit status of the process. (only valid if state == DONE) 
276         @see Runtime#state */
277     public final int exitStatus() {
278         if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state");
279         return exitStatus;
280     }
281     
282     /** Runs the process until it exits and returns the exit status.
283         If the process executes the PAUSE syscall execution will be paused for 500ms and a warning will be displayed */
284     public final int run(String[] args) throws ExecutionException {
285         start(args);
286         for(;;) {
287             if(execute()) break;
288             System.err.println("WARNING: Pause requested while executing run()");
289             try { Thread.sleep(500); } catch(InterruptedException e) { }
290         }
291         return exitStatus();
292     }
293     
294     /** Adds the String[] array, <i>args</i>, to the arguments page in main memory */
295     private void addArgs(String[] args) throws ExecutionException {
296         int count = args.length;
297         byte[] nullTerminator = new byte[1];
298         int total = 4; /* null last table entry  */
299         for(int i=0;i<count;i++) total += args[i].length() + 1/*null terminator*/ + 4/*table entry*/;
300         if(total >= PAGE_SIZE-4) throw new ExecutionException("Arguments too large");
301         int start = ARGS_ADDR;
302         int addr = start + (count+1)*4;
303         int[] table = new int[count+1];
304         for(int i=0;i<count;i++) {
305             byte[] a;
306             try { a = args[i].getBytes("US-ASCII"); } catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
307             table[i] = addr;
308             copyout(a,addr,a.length);
309             addr += a.length;
310             copyout(nullTerminator,addr,1);
311             addr += 1;
312         }
313         addr=start;
314         for(int i=0;i<count;i++) {
315             memWrite(addr,table[i]);
316             addr += 4;
317         }
318     }
319     
320     /** Sets word number <i>index</i> in the _user_info table to <i>word</i> 
321         _user_info is a 4096 byte table in the process's memory. It contains 1024 32-bit entries. This 
322         can be used by the process to communicate with the caller of the process. Each entry is 32-bit
323         wide and can be used to store integers or pointers */
324     public void setUserInfo(int index, int word) {
325         if(index < 0 ||  index >= 1024) throw new IllegalStateException("setUserInfo called with index >= 1024");
326         try {
327             memWrite(USER_INFO_ADDR+index*4,word);
328         } catch(FaultException e) { throw new Error("should never happen: " + e); }
329     }
330     
331     /** Returns the word in the _user_info table entry <i>index</i>
332         @see Runtime#setUserInfo(int,int) setUserInfo */
333     public int getUserInfo(int index) {
334         if(index < 0 ||  index >= 1024) throw new IllegalStateException("setUserInfo called with index >= 1024");
335         try {
336             return memRead(USER_INFO_ADDR+index*4);
337         } catch(FaultException e) { throw new Error("should never happen: " + e); }
338     }
339     
340     /** Executes the process until the PAUSE syscall is invoked or the process exits. Returns true if the process exited. */
341     public final boolean execute() throws ExecutionException {
342         if(state == PAUSED) state = RUNNING;
343         if(state != RUNNING) throw new IllegalStateException("execute() called in inappropriate state");
344         _execute();
345         if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state");
346         return state == DONE;
347     }
348     
349     /** Initializes the process and prepairs it to be executed with execute() */
350     public final void start(String[] args) throws ExecutionException {
351         if(state != INITIALIZED) throw new IllegalStateException("start() called in inappropriate state");
352         _start(entryPoint);
353         addArgs(args);
354         fds = new FileDescriptor[OPEN_MAX];
355         fds[0] = new InputStreamFD(System.in)   { public boolean isatty() { return true; } };
356         fds[1] = new OutputStreamFD(System.out) { public boolean isatty() { return true; } };
357         fds[2] = new OutputStreamFD(System.err) { public boolean isatty() { return true; } };
358         state = PAUSED;
359     }
360     
361     /** Determines if the process can access <i>fileName</i>. The default implementation simply logs 
362         the request and allows it */
363     protected boolean allowFileAccess(String fileName, boolean write) {
364         System.err.println("Allowing " + (write?"write":"read-only") + " to " + fileName);
365         return true;
366     }
367     
368     /** Allocated an entry in the FileDescriptor table for <i>fd</i> and returns the number.
369         Returns -1 if the table is full. This can be used by subclasses to use custom file
370         descriptors */
371     protected int allocFDEnt(FileDescriptor fd) {
372         int i;
373         for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
374         if(i==OPEN_MAX) return -1;
375         fds[i] = fd;
376         return i;
377     }
378
379     /** The open syscall */
380     private int sys_open(int addr, int flags, int mode) {
381         final int O_RDONLY = 0;
382         final int O_WRONLY = 1;
383         final int O_RDWR = 2;
384         final int O_APPEND = 0x0008;
385         final int O_CREAT = 0x0200;
386         final int O_NONBLOCK = 0x4000;
387         final int O_EXCL = 0x0800;
388         
389         if((flags & O_APPEND) != 0) {
390             System.err.println("WARNING: O_APPEND not supported");
391             return -EOPNOTSUPP;
392         }
393         if((flags & O_NONBLOCK) != 0) {
394             System.err.println("WARNING: O_NONBLOCK not supported");
395             return -EOPNOTSUPP;
396         }
397
398         try {
399             String fileName = cstring(addr);
400             if(!allowFileAccess(fileName,(flags&3)!=0)) return -EACCES;
401
402             File f = new File(fileName);
403             if((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)
404                 if(!f.createNewFile()) return -EEXIST;
405             if(f.exists()) {
406                 if(f.length() >= Integer.MAX_VALUE) return -EOPNOTSUPP;
407             } else {
408                 if((flags & O_CREAT) == 0) return -ENOENT;
409             }
410             int fdn = allocFDEnt(new RegularFileDescriptor(f,flags&3));
411             return fdn == -1 ? -ENFILE : fdn;
412         } catch(FaultException e) {
413             return -EFAULT;
414         } catch(FileNotFoundException e) { 
415             if(e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
416             return -ENOENT;
417         } catch(IOException e) {
418             return -EIO;
419         }
420     }
421
422     /** The write syscall */
423     private int sys_write(int fdn, int addr, int count) {
424         int n = 0;
425         int r;
426         FileDescriptor fd;
427         count = Math.min(count,MAX_CHUNK);
428         try {
429             fd = fds[fdn];
430             if(fd == null || !fd.writable()) return -EBADFD;
431         } catch(ArrayIndexOutOfBoundsException e) {
432             return -EBADFD;
433         }
434         try {
435             byte[] buf = byteBuf(count);
436             copyin(addr,buf,count);
437             return fd.write(buf,0,count);
438         } catch(FaultException e) {
439             System.err.println(e);
440             return -EFAULT;
441         } catch(IOException e) {
442             System.err.println(e);
443             return -EIO;
444         }
445     }
446
447     /** The read syscall */
448     private int sys_read(int fdn, int addr, int count) {
449         FileDescriptor fd;
450         count = Math.min(count,MAX_CHUNK);
451         try {
452             fd = fds[fdn];
453             if(fd == null || !fd.readable()) return -EBADFD;
454         } catch(ArrayIndexOutOfBoundsException e) {
455             return -EBADFD;
456         }
457         try {
458             byte[] buf = byteBuf(count);
459             int n = fd.read(buf,0,count);
460             copyout(buf,addr,n);
461             return n;
462         } catch(FaultException e) {
463             System.err.println(e);
464             return -EFAULT;
465         } catch(IOException e) {
466             System.err.println(e);
467             return -EIO;
468         }
469     }
470     
471     /** The close syscall */
472     private int sys_close(int fdn) {
473         FileDescriptor fd;
474         try {
475             fd = fds[fdn];
476             if(fd == null) return -EBADFD;
477         } catch(ArrayIndexOutOfBoundsException e) {
478             return -EBADFD;
479         }
480         fds[fdn] = null;
481         fd.close();
482         return 0;
483     }
484     
485     /** The seek syscall */
486     private int sys_seek(int fdn, int offset, int whence) {
487         FileDescriptor fd;
488         try {
489             fd = fds[fdn];
490             if(fd == null || !fd.readable()) return -EBADFD;
491         } catch(ArrayIndexOutOfBoundsException e) {
492             return -EBADFD;
493         }
494         if(whence != FileDescriptor.SEEK_SET && whence !=  FileDescriptor.SEEK_CUR && whence !=  FileDescriptor.SEEK_END) return -EINVAL;
495         try {
496             int n = fd.seek(offset,whence);
497             return n < 0 ? -ESPIPE : n;
498         } catch(IOException e) {
499             return -ESPIPE;
500         }
501     }
502     
503     /** The stat/fstat syscall helper */
504     private int stat(FileInfo fi, int addr) {
505         int size = fi.size();
506         try {
507             memWrite(addr+0,(1<<16)|1); // st_dev (top 16), // st_ino (bottom 16)
508             memWrite(addr+4,(fi.type() & 0xf000)|0644); // st_mode
509             memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16)
510             memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16)
511             memWrite(addr+16,size); // st_size
512             memWrite(addr+20,0); // st_atime
513             // memWrite(addr+24,0) // st_spare1
514             memWrite(addr+28,(int)(fi.modTime()/1000)); // st_mtime
515             // memWrite(addr+32,0) // st_spare2
516             memWrite(addr+36,0); // st_ctime
517             // memWrite(addr+40,0) // st_spare3
518             memWrite(addr+44,512); // st_bklsize;
519             memWrite(addr+48,(size+511)&(~511)); // st_blocks
520             // memWrite(addr+52,0) // st_spare4[0]
521             // memWrite(addr+56,0) // st_spare4[1]
522         } catch(FaultException e) {
523             System.err.println(e);
524             return -EFAULT;
525         }
526         return 0;
527     }
528     
529     /** The fstat syscall */
530     private int sys_fstat(int fdn, int addr) {
531         FileDescriptor fd;
532         try {
533             fd = fds[fdn];
534             if(fd == null) return -EBADFD;
535         } catch(ArrayIndexOutOfBoundsException e) {
536             return -EBADFD;
537         }
538         return stat(fd.fileInfo(),addr);
539     }
540     
541     /** The sbrk syscall. This can also be used by subclasses to allocate memory.
542         <i>incr</i> is how much to increase the break by */
543     public int sbrk(int incr) {
544         if(incr==0) return brk<<PAGE_SHIFT;
545         int oldBrk = brk;
546         int newBrk = oldBrk + ((incr+PAGE_SIZE-1)>>PAGE_SHIFT);
547         if(newBrk >= BRK_LIMIT) {
548             System.err.println("Hit BRK_LIMIT");
549             return -ENOMEM;
550         }
551         for(int i=oldBrk;i<newBrk;i++)
552             readPages[i] = writePages[i] = emptyPage();
553         brk = newBrk;
554         return oldBrk<<PAGE_SHIFT;
555     }
556         
557     /** The kill syscall.
558        SIGSTOP, SIGTSTO, SIGTTIN, and SIGTTOUT pause the process.
559        SIGCONT, SIGCHLD, SIGIO, and SIGWINCH are ignored.
560        Anything else terminates the process. */
561     private int sys_kill(int pid, int signal) {
562         // This will only be called by raise() in newlib to invoke the default handler
563         // We don't have to worry about actually delivering the signal
564         if(pid != PID) return -ESRCH;
565         if(signal < 0 || signal >= 32) return -EINVAL;
566         switch(signal) {
567             case 0: return 0;
568             case 17: // SIGSTOP
569             case 18: // SIGTSTP
570             case 21: // SIGTTIN
571             case 22: // SIGTTOU
572                 state = PAUSED;
573                 break;
574             case 19: // SIGCONT
575             case 20: // SIGCHLD
576             case 23: // SIGIO
577             case 28: // SIGWINCH
578                 break;
579             default: {
580                 String msg = "Terminating on signal: " + signal + "\n";
581                 exitStatus = 1;
582                 state = DONE;
583                 if(fds[2]==null) {
584                     System.out.print(msg);
585                 } else {
586                     try {
587                         byte[] b = msg.getBytes("US-ASCII"); 
588                         fds[2].write(b,0,b.length);
589                     }
590                     catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
591                     catch(IOException e) { }
592                 }
593             }
594         }
595         return 0;
596     }
597     
598     /** The getpid syscall */
599     private int sys_getpid() { return PID; }
600     
601     /** The syscall dispatcher.
602         The should be called by subclasses when the syscall instruction is invoked.
603         <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 
604         the contenst of A0, A1, A2, and A3. The call MAY change the state
605         @see Runtime#state state */
606     protected int syscall(int syscall, int a, int b, int c, int d) {
607         switch(syscall) {
608             case SYS_null: return 0;
609             case SYS_exit: exitStatus = a; state = DONE; return 0;
610             case SYS_pause: state = PAUSED; return 0;
611             case SYS_write: return sys_write(a,b,c);
612             case SYS_fstat: return sys_fstat(a,b);
613             case SYS_sbrk: return sbrk(a);
614             case SYS_open: return sys_open(a,b,c);
615             case SYS_close: return sys_close(a);
616             case SYS_read: return sys_read(a,b,c);
617             case SYS_seek: return sys_seek(a,b,c);
618             case SYS_kill: return sys_kill(a,b);
619             case SYS_getpid: return sys_getpid();
620             default:
621                 System.err.println("Attempted to use unknown syscall: " + syscall);
622                 return -ENOSYS;
623         }
624     }
625     
626     /** Helper function to read a cstring from main memory */
627     public String cstring(int addr) throws ReadFaultException {
628         StringBuffer sb = new StringBuffer();
629         for(;;) {
630             int word = memRead(addr&~3);
631             switch(addr&3) {
632                 case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++;
633                 case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++;
634                 case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++;
635                 case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++;
636             }
637         }
638     }
639     
640     /** FileInfo class - used by stat */
641     public static class FileInfo {
642         public static final int S_IFIFO = 0010000;
643         public static final int S_FCHR =  0020000;
644         public static final int S_IFDIR = 0040000;
645         public static final int S_IFREG = 0100000;
646         
647         public int size() { return 0; }
648         public int type() { return S_IFIFO; }
649         public long modTime() { return 0; }
650     }
651     
652     /** FileInfo subclass for normal files */
653     public static class FileFileInfo extends FileInfo {
654         public File f;
655         public FileFileInfo(File f) { this.f = f; }
656         public int size() { return (int)f.length(); }
657         public int type() { return f.isDirectory() ? S_IFDIR : S_IFREG; }
658         public long modTime() { return f.lastModified(); }
659     }
660
661     /** File Descriptor class */
662     public static abstract class FileDescriptor {
663         protected final static int SEEK_SET = 0;
664         protected final static int SEEK_CUR = 1;
665         protected final static int SEEK_END = 2;
666     
667         /** returns true if the fd is readable */
668         public boolean readable() { return false; }
669         /** returns true if the fd is writable */
670         public boolean writable() { return false; }
671         
672         private static final FileInfo nullFi = new FileInfo();
673         private FileInfo fi;
674         public FileInfo fileInfo() { return fi; }
675         
676         /** Initializes the FileDescriptor with no FileInfo struct */
677         FileDescriptor() { this(null); }
678         /** Initializes the FileDescriptor with the given FileInfo struct (used by fstat) */
679         FileDescriptor(FileInfo fi) { this.fi = fi==null ? nullFi : fi; }
680         
681         /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */
682         public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
683         /** Write. Should return the number of bytes written or throw an IOException on error */
684         public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
685
686         /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */
687         public int seek(int n, int whence)  throws IOException  { return -1; }
688         
689         /** Should return true if this is a tty */
690         public boolean isatty() { return false; }
691         
692         /** Closes the fd */
693         void close() { }
694     }
695     
696     /** FileDescriptor class for normal files */
697     public static class RegularFileDescriptor extends FileDescriptor {
698         private int mode;
699         private RandomAccessFile raf;
700         public boolean readable() { return mode != 1; }
701         public boolean writable() { return mode != 0; }
702         
703         RegularFileDescriptor(File f,int m) throws IOException {
704             super(new FileFileInfo(f));
705             String mode = m == 0 ? "r" : "rw";
706             this.mode = m;
707             raf = new RandomAccessFile(f,mode);
708             if(raf.length() >= Integer.MAX_VALUE) throw new IOException("File too large");
709         }
710         
711         public int seek(int n_, int whence) throws IOException {
712             long n = n_;
713             switch(whence) {
714                 case SEEK_SET: break;
715                 case SEEK_CUR: n += raf.getFilePointer(); break;
716                 case SEEK_END: n += raf.length(); break;
717                 default: return -1;
718             }
719             raf.seek(n);
720             return (int)n;
721         }
722         
723         public int write(byte[] a, int off, int length) throws IOException { raf.write(a,off,length); return length; }
724         public int read(byte[] a, int off, int length) throws IOException { int n = raf.read(a,off,length); return n < 0 ? 0 : n; }
725         
726         void close() { try { raf.close(); } catch(Exception e) { } }
727     }
728     
729     public class OutputStreamFD extends FileDescriptor {
730         private OutputStream os;
731         public boolean writable() { return true; }
732         public OutputStreamFD(OutputStream os) { this.os = os; }
733         public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; }
734     }
735     
736     public class InputStreamFD extends FileDescriptor {
737         private InputStream is;
738         public boolean readable() { return true; }
739         public InputStreamFD(InputStream is) { this.is = is; }
740         public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
741     }
742
743     // Exceptions
744     public static class ReadFaultException extends FaultException {
745         public ReadFaultException(int addr) { super(addr); }
746     }
747     public static class WriteFaultException extends FaultException {
748         public WriteFaultException(int addr) { super(addr); }
749     }
750     public static abstract class FaultException extends ExecutionException {
751         private int addr;
752         public FaultException(int addr) { this.addr = addr; }
753         public String getMessage() { return "fault at: " + toHex(addr); }
754     }
755     public static class ExecutionException extends Exception {
756         public ExecutionException() { }
757         public ExecutionException(String s) { super(s); }
758     }
759     
760     // Utility functions
761     private byte[] byteBuf(int size) {
762         if(_byteBuf==null) _byteBuf = new byte[size];
763         else if(_byteBuf.length < size)
764             _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)];
765         return _byteBuf;
766     }
767     
768     protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
769     protected final static int min(int a, int b) { return a < b ? a : b; }
770     protected final static int max(int a, int b) { return a > b ? a : b; }
771 }