1 // Copyright 2003 Brian Alliet
2 // Based on org.xwt.imp.MIPS by Adam Megacz
3 // Portions Copyright 2003 Adam Megacz
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;
21 <-- ((TOTAL_PAGES-0)*PAGE_SIZE) -->
23 <-- ((TOTAL_PAGES-1)*PAGE_SIZE) -->
25 <-- ((TOTAL_PAGES-2)*PAGE_SIZE) -->
27 <-- ((TOTAL_PAGES-3)*PAGE_SIZE) -->
29 <-- ((TOTAL_PAGES-4)*PAGE_SIZE) -->
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;
39 /** The initial stack pointer address */
40 protected final static int INITIAL_SP = STUFF_BASE - PAGE_SIZE;
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];
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]; }
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;
59 /** The current break between the heap and unallocated memory and the stack.
60 This is the page number NOT an address */
63 /** The program's entry point */
64 protected int entryPoint;
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;
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; }
82 /** The exit status if the process (only valid if state==DONE)
84 protected int exitStatus;
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;
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;
97 /** The pid of this "process" */
98 public static final int PID = 1;
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;
105 /** This should setup the system to begin executing at pc and
106 initialize the cpu registers as follows
107 K0 (r26) = STUFF_BASE
109 SP (r29) = INITIAL_SP
110 RA (r31) = 0xdeadbeef
112 protected abstract void _start(int pc);
114 /** Initialize the Runtime with empty pages disabled
115 @see Runtime#Runtime(boolean) */
116 public Runtime() { this(false); }
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();
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) {
137 if(writePages[page] == null) writePages[page] = readPages[page];
139 System.arraycopy(src,i,readPages[page],start,elements);
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();
154 if(writePages[page] == null) writePages[page] = readPages[page];
155 for(int j=start;j<start+elements;j++) writePages[page][j] = 0;
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 {
167 int word = memRead(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;
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) {
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);
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);
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 {
200 int word = memRead(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;
206 memWrite(addr&~3,word);
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;
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); }
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);
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
243 } catch(NullPointerException e) {
244 throw new ReadFaultException(addr);
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);
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
260 writePages[page][entry] = value;
261 } catch(NullPointerException e) {
262 throw new WriteFaultException(addr);
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;
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");
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 {
288 System.err.println("WARNING: Pause requested while executing run()");
289 try { Thread.sleep(500); } catch(InterruptedException e) { }
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++) {
306 try { a = args[i].getBytes("US-ASCII"); } catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
308 copyout(a,addr,a.length);
310 copyout(nullTerminator,addr,1);
314 for(int i=0;i<count;i++) {
315 memWrite(addr,table[i]);
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");
327 memWrite(USER_INFO_ADDR+index*4,word);
328 } catch(FaultException e) { throw new Error("should never happen: " + e); }
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");
336 return memRead(USER_INFO_ADDR+index*4);
337 } catch(FaultException e) { throw new Error("should never happen: " + e); }
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");
345 if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state");
346 return state == DONE;
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");
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; } };
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);
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
371 protected int allocFDEnt(FileDescriptor fd) {
373 for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
374 if(i==OPEN_MAX) return -1;
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;
389 if((flags & O_APPEND) != 0) {
390 System.err.println("WARNING: O_APPEND not supported");
393 if((flags & O_NONBLOCK) != 0) {
394 System.err.println("WARNING: O_NONBLOCK not supported");
399 String fileName = cstring(addr);
400 if(!allowFileAccess(fileName,(flags&3)!=0)) return -EACCES;
402 File f = new File(fileName);
403 if((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)
404 if(!f.createNewFile()) return -EEXIST;
406 if(f.length() >= Integer.MAX_VALUE) return -EOPNOTSUPP;
408 if((flags & O_CREAT) == 0) return -ENOENT;
410 int fdn = allocFDEnt(new RegularFileDescriptor(f,flags&3));
411 return fdn == -1 ? -ENFILE : fdn;
412 } catch(FaultException e) {
414 } catch(FileNotFoundException e) {
415 if(e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
417 } catch(IOException e) {
422 /** The write syscall */
423 private int sys_write(int fdn, int addr, int count) {
427 count = Math.min(count,MAX_CHUNK);
430 if(fd == null || !fd.writable()) return -EBADFD;
431 } catch(ArrayIndexOutOfBoundsException e) {
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);
441 } catch(IOException e) {
442 System.err.println(e);
447 /** The read syscall */
448 private int sys_read(int fdn, int addr, int count) {
450 count = Math.min(count,MAX_CHUNK);
453 if(fd == null || !fd.readable()) return -EBADFD;
454 } catch(ArrayIndexOutOfBoundsException e) {
458 byte[] buf = byteBuf(count);
459 int n = fd.read(buf,0,count);
462 } catch(FaultException e) {
463 System.err.println(e);
465 } catch(IOException e) {
466 System.err.println(e);
471 /** The close syscall */
472 private int sys_close(int fdn) {
476 if(fd == null) return -EBADFD;
477 } catch(ArrayIndexOutOfBoundsException e) {
485 /** The seek syscall */
486 private int sys_seek(int fdn, int offset, int whence) {
490 if(fd == null || !fd.readable()) return -EBADFD;
491 } catch(ArrayIndexOutOfBoundsException e) {
494 if(whence != FileDescriptor.SEEK_SET && whence != FileDescriptor.SEEK_CUR && whence != FileDescriptor.SEEK_END) return -EINVAL;
496 int n = fd.seek(offset,whence);
497 return n < 0 ? -ESPIPE : n;
498 } catch(IOException e) {
503 /** The stat/fstat syscall helper */
504 private int stat(FileInfo fi, int addr) {
505 int size = fi.size();
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);
529 /** The fstat syscall */
530 private int sys_fstat(int fdn, int addr) {
534 if(fd == null) return -EBADFD;
535 } catch(ArrayIndexOutOfBoundsException e) {
538 return stat(fd.fileInfo(),addr);
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;
546 int newBrk = oldBrk + ((incr+PAGE_SIZE-1)>>PAGE_SHIFT);
547 if(newBrk >= BRK_LIMIT) {
548 System.err.println("Hit BRK_LIMIT");
551 for(int i=oldBrk;i<newBrk;i++)
552 readPages[i] = writePages[i] = emptyPage();
554 return oldBrk<<PAGE_SHIFT;
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;
580 String msg = "Terminating on signal: " + signal + "\n";
584 System.out.print(msg);
587 byte[] b = msg.getBytes("US-ASCII");
588 fds[2].write(b,0,b.length);
590 catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
591 catch(IOException e) { }
598 /** The getpid syscall */
599 private int sys_getpid() { return PID; }
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) {
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();
621 System.err.println("Attempted to use unknown syscall: " + syscall);
626 /** Helper function to read a cstring from main memory */
627 public String cstring(int addr) throws ReadFaultException {
628 StringBuffer sb = new StringBuffer();
630 int word = memRead(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++;
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;
647 public int size() { return 0; }
648 public int type() { return S_IFIFO; }
649 public long modTime() { return 0; }
652 /** FileInfo subclass for normal files */
653 public static class FileFileInfo extends FileInfo {
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(); }
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;
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; }
672 private static final FileInfo nullFi = new FileInfo();
674 public FileInfo fileInfo() { return fi; }
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; }
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"); }
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; }
689 /** Should return true if this is a tty */
690 public boolean isatty() { return false; }
696 /** FileDescriptor class for normal files */
697 public static class RegularFileDescriptor extends FileDescriptor {
699 private RandomAccessFile raf;
700 public boolean readable() { return mode != 1; }
701 public boolean writable() { return mode != 0; }
703 RegularFileDescriptor(File f,int m) throws IOException {
704 super(new FileFileInfo(f));
705 String mode = m == 0 ? "r" : "rw";
707 raf = new RandomAccessFile(f,mode);
708 if(raf.length() >= Integer.MAX_VALUE) throw new IOException("File too large");
711 public int seek(int n_, int whence) throws IOException {
714 case SEEK_SET: break;
715 case SEEK_CUR: n += raf.getFilePointer(); break;
716 case SEEK_END: n += raf.length(); break;
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; }
726 void close() { try { raf.close(); } catch(Exception e) { } }
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; }
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; }
744 public static class ReadFaultException extends FaultException {
745 public ReadFaultException(int addr) { super(addr); }
747 public static class WriteFaultException extends FaultException {
748 public WriteFaultException(int addr) { super(addr); }
750 public static abstract class FaultException extends ExecutionException {
752 public FaultException(int addr) { this.addr = addr; }
753 public String getMessage() { return "fault at: " + toHex(addr); }
755 public static class ExecutionException extends Exception {
756 public ExecutionException() { }
757 public ExecutionException(String s) { super(s); }
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)];
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; }