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        public final static int PAGE_SIZE = 4096;
10       public final static int PAGE_WORDS = (int)(PAGE_SIZE >>> 2);
11       public 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  }
772