+// Copyright 2003 Brian Alliet
+// Based on org.xwt.imp.MIPS by Adam Megacz
+// Portions Copyright 2003 Adam Megacz
package org.xwt.mips;
import java.io.*;
-public abstract class VM implements Syscalls, Errno {
- // Register Names
- protected final static int ZERO = 0; // Immutable, hardwired to 0
- protected final static int AT = 1; // Reserved for assembler
- protected final static int K0 = 26; // Reserved for kernel
- protected final static int K1 = 27; // Reserved for kernel
- protected final static int GP = 28; // Global pointer (the middle of .sdata/.sbss)
- protected final static int SP = 29; // Stack pointer
- protected final static int FP = 30; // Frame Pointer
- protected final static int RA = 31; // Return Address
-
- // Return values (caller saved)
- protected final static int V0 = 2;
- protected final static int V1 = 3;
- // Argument Registers (caller saved)
- protected final static int A0 = 4;
- protected final static int A1 = 5;
- protected final static int A2 = 6;
- protected final static int A3 = 7;
- // Temporaries (caller saved)
- protected final static int T0 = 8;
- protected final static int T1 = 9;
- protected final static int T2 = 10;
- protected final static int T3 = 11;
- protected final static int T4 = 12;
- protected final static int T5 = 13;
- protected final static int T6 = 14;
- protected final static int T7 = 15;
- protected final static int T8 = 24;
- protected final static int T9 = 25;
- // Saved (callee saved)
- protected final static int S0 = 16;
- protected final static int S1 = 17;
- protected final static int S2 = 18;
- protected final static int S3 = 19;
- protected final static int S4 = 20;
- protected final static int S5 = 21;
- protected final static int S6 = 22;
- protected final static int S7 = 23;
-
- // Page Constants
- // Page Size: 4k
- // Total Pages: 64k
- // Maxiumum Addressable memory 256mb
- // 1mb of stack space
- protected final static int PAGE_SIZE = 4096;
- protected final static int PAGE_WORDS = (int)(PAGE_SIZE >>> 2);
- protected final static int PAGE_SHIFT = 12;
- protected final static int STACK_PAGES = 256;
- // NOTE: If you change TOTAL_PAGES crt0.c needs to be updated to reflect the
- // new location of INITIAL_SP
+public abstract class Runtime implements Syscalls, Errno,Registers {
+ /** Pages are 4k in size */
+ final static int PAGE_SIZE = 4096;
+ final static int PAGE_WORDS = (int)(PAGE_SIZE >>> 2);
+ final static int PAGE_SHIFT = 12;
+ /** There are 65536 pages available for a total of 256mb of addressable memory */
protected final static int TOTAL_PAGES = 65536;
- protected final static int BRK_LIMIT = 32768;
- // Top page is always empty
- // next page down contains command line arguments
- protected final static int ARGS_ADDR = (TOTAL_PAGES-2)*PAGE_SIZE;
- // next page down contains _user_info data
- protected final static int USER_INFO_ADDR = (TOTAL_PAGES-3)*PAGE_SIZE;
- // next page down is the start of the stack
- protected final static int INITIAL_SP = (TOTAL_PAGES-3)*PAGE_SIZE;
- // magic page that signified an allocated but empty (untouched) page
- private final static int[] emptyPage = new int[0];
-
- // Main memory
+ /** The top 256 pages are reserved for the stack. Arguments and userdata info use up the first few pages */
+ protected final static int STACK_PAGES = 256;
+ /** This is the upper limit of the pages allocated by the brk() syscall. */
+ protected final static int BRK_LIMIT = TOTAL_PAGES - STACK_PAGES - 1024;
+
+ /* High memory layout
+ TOP
+ <-- ((TOTAL_PAGES-0)*PAGE_SIZE) -->
+ Empty Page
+ <-- ((TOTAL_PAGES-1)*PAGE_SIZE) -->
+ Args (1 page)
+ <-- ((TOTAL_PAGES-2)*PAGE_SIZE) -->
+ User info (1 page)
+ <-- ((TOTAL_PAGES-3)*PAGE_SIZE) -->
+ Empty page
+ <-- ((TOTAL_PAGES-4)*PAGE_SIZE) -->
+ Stack top
+ */
+
+ /** The base address for the args and user_info (this will be passed to crt0.c)
+ The args must be at STUFF_BASE+1 page and user_info must be at STUFF_BASE */
+ protected final static int STUFF_BASE = (TOTAL_PAGES-3)*PAGE_SIZE;
+ protected final static int ARGS_ADDR = STUFF_BASE + PAGE_SIZE;
+ protected final static int USER_INFO_ADDR = STUFF_BASE;
+
+ /** The initial stack pointer address */
+ protected final static int INITIAL_SP = STUFF_BASE - PAGE_SIZE;
+
+ /** True if we allow empty pages (_emptyPage) to exist in memory.
+ Empty pages are pages which are allocated by the program but do not contain any
+ data yet (they are all 0s). If empty pages are allowed subclasses must always
+ access main memory with the memRead and memWrite functions */
+ private final boolean allowEmptyPages;
+ /** the "empty page" */
+ private final static int[] _emptyPage = new int[0];
+
+ /** Returns a new empty page (_emptyPage is empty pages are enabled or a new zero'd page) */
+ private final int[] emptyPage() { return allowEmptyPages ? _emptyPage : new int[PAGE_WORDS]; }
+
+ /** Readable main memory pages */
protected final int[][] readPages;
+ /** Writable main memory pages.
+ If the page is writable writePages[x] == readPages[x]; if not writePages[x] == null. */
protected final int[][] writePages;
- // Brk
- protected int brk; // PAGE not address
+ /** The current break between the heap and unallocated memory and the stack.
+ This is the page number NOT an address */
+ protected int brk;
- // Entry point - what start() sets pc to
+ /** The program's entry point */
protected int entryPoint;
- // State constants
- public final static int UNINITIALIZED = 0;
+ /** State contant: There is no program loaded in memory */
+ public final static int UNINITIALIZED = 0;
+ /** Text/Data loaded in memory */
public final static int INITIALIZED = 1;
+ /** Program is executing instructions */
public final static int RUNNING = 2;
+ /** Prgram has been started but is paused */
public final static int PAUSED = 3;
+ /** Program has exited (it cannot currently be restarted) */
public final static int DONE = 4;
- // State
+ /** The current state (UNINITIALIZED, INITIALIZED, RUNNING, PAUSED, or DONE) */
protected int state = UNINITIALIZED;
+ /** @see Runtime#state state */
public final int getState() { return state; }
+
+ /** The exit status if the process (only valid if state==DONE)
+ @see Runtime#state */
protected int exitStatus;
- // File descriptors
+ /** Maximum number of open file descriptors */
private final static int OPEN_MAX = 256;
+ /** Table containing all open file descriptors. (Entries are null if the fd is not in use */
private FileDescriptor[] fds;
- // Temporary buffer for read/write operations
+ /** Temporary buffer for read/write operations */
private byte[] _byteBuf = null;
- private final static int MAX_CHUNK = 4*1024*1024-8;
+ /** Max size of temporary buffer
+ @see Runtime#_byteBuf */
+ private final static int MAX_CHUNK = 4*1024*1024;
- // Abstract methods
- // This should start executing at pc
- public abstract void execute() throws EmulationException;
- // This should initialize the cpu registers to point to the entry point
- protected abstract void _start(int pc);
-
+ /** The pid of this "process" */
public static final int PID = 1;
+
+ /** Subclasses should actually execute program in this method. They should continue
+ executing until state != RUNNING. Only syscall() can modify state. It is safe
+ to only check the state attribyte after a call to syscall() */
+ protected abstract void _execute() throws ExecutionException;
+
+ /** This should setup the system to begin executing at pc and
+ initialize the cpu registers as follows
+ K0 (r26) = STUFF_BASE
+ K1 (r27) = PAGE_SIZE
+ SP (r29) = INITIAL_SP
+ RA (r31) = 0xdeadbeef
+ */
+ protected abstract void _start(int pc);
- public VM() {
+ /** Initialize the Runtime with empty pages disabled
+ @see Runtime#Runtime(boolean) */
+ public Runtime() { this(false); }
+
+ /** Initialize the Runtime. Empty pages are enabled if <i>allowEmptyPages</i> is set */
+ public Runtime(boolean allowEmptyPages) {
+ this.allowEmptyPages = allowEmptyPages;
readPages = new int[TOTAL_PAGES][];
writePages = new int[TOTAL_PAGES][];
for(int i=0;i<STACK_PAGES;i++)
- readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage;
+ readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage();
+ }
+
+ /** Copy everything from <i>src</i> to <i>addr</i> initializing uninitialized pages if required.
+ Newly initalized pages will be marked read-only if <i>ro</i> is set */
+ protected final void initPages(int[] src, int addr, boolean ro) {
+ for(int i=0;i<src.length;) {
+ int page = addr >>> PAGE_SHIFT;
+ int start = (addr&(PAGE_SIZE-1))>>2;
+ int elements = min(PAGE_WORDS-start,src.length-i);
+ if(readPages[page]==null) {
+ initPage(page,ro);
+ } else if(!ro) {
+ if(writePages[page] == null) writePages[page] = readPages[page];
+ }
+ System.arraycopy(src,i,readPages[page],start,elements);
+ i += elements;
+ addr += elements*4;
+ }
}
- public void copyin(int addr, byte[] a, int length) throws ReadFaultException {
+ /** Initialize <i>words</i> of pages starting at <i>addr</i> to 0 */
+ protected final void clearPages(int addr, int words) {
+ for(int i=0;i<words;) {
+ int page = addr >>> PAGE_SHIFT;
+ int start = (addr&(PAGE_SIZE-1))>>2;
+ int elements = min(PAGE_WORDS-start,words-i);
+ if(readPages[page]==null) {
+ readPages[page] = writePages[page] = emptyPage();
+ } else {
+ if(writePages[page] == null) writePages[page] = readPages[page];
+ for(int j=start;j<start+elements;j++) writePages[page][j] = 0;
+ }
+ i += elements;
+ addr += elements*4;
+ }
+ }
+
+ /** Copies <i>length</i> bytes from the processes memory space starting at
+ <i>addr</i> INTO a java byte array <i>a</i> */
+ public final void copyin(int addr, byte[] a, int length) throws ReadFaultException {
int n=0;
if((addr&3)!=0) {
int word = memRead(addr&~3);
- switch(addr&3) {
+ switch(addr&3) {
case 1: a[n++] = (byte)((word>>>16)&0xff); if(length-n==0) break;
case 2: a[n++] = (byte)((word>>> 8)&0xff); if(length-n==0) break;
case 3: a[n++] = (byte)((word>>> 0)&0xff); if(length-n==0) break;
int end = start + (min(PAGE_SIZE-(addr&(PAGE_SIZE-1)),(length-n)&~3) >> 2);
int[] page = readPages[addr >>> PAGE_SHIFT];
if(page == null) throw new ReadFaultException(addr);
- if(page == emptyPage) { addr+=(end-start)<<2; n+=(end-start)<<2; continue; }
+ if(page == _emptyPage) { addr+=(end-start)<<2; n+=(end-start)<<2; continue; }
for(int i=start;i<end;i++,addr+=4) {
int word = page[i];
a[n++] = (byte)((word>>>24)&0xff); a[n++] = (byte)((word>>>16)&0xff);
}
if(length-n > 0) {
int word = memRead(addr);
- if(length-n >= 1) a[n] = (byte)((word>>>24)&0xff);
+ if(length-n >= 1) a[n+0] = (byte)((word>>>24)&0xff);
if(length-n >= 2) a[n+1] = (byte)((word>>>16)&0xff);
if(length-n >= 3) a[n+2] = (byte)((word>>> 8)&0xff);
}
}
- public void copyout(byte[] a, int addr, int length) throws FaultException {
+ /** Copies <i>length</i> bytes OUT OF the java array <i>a</i> into the processes memory
+ space at <i>addr</i> */
+ public final void copyout(byte[] a, int addr, int length) throws FaultException {
int n=0;
if((addr&3)!=0) {
int word = memRead(addr&~3);
- switch(addr&3) {
+ switch(addr&3) {
case 1: word = (word&0xff00ffff)|((a[n]&0xff)<<16); n++; if(length-n==0) break;
case 2: word = (word&0xffff00ff)|((a[n]&0xff)<< 8); n++; if(length-n==0) break;
case 3: word = (word&0xffffff00)|((a[n]&0xff)<< 0); n++; if(length-n==0) break;
int end = start + (min(PAGE_SIZE-(addr&(PAGE_SIZE-1)),(length-n)&~3) >> 2);
int[] page = writePages[addr >>> PAGE_SHIFT];
if(page == null) throw new WriteFaultException(addr);
- if(page == emptyPage) { memWrite(addr,0); page = writePages[addr >>> PAGE_SHIFT]; }
+ if(page == _emptyPage) { memWrite(addr,0); page = writePages[addr >>> PAGE_SHIFT]; }
for(int i=start;i<end;i++,addr+=4) {
int word = ((a[n+0]&0xff)<<24)|((a[n+1]&0xff)<<16)|((a[n+2]&0xff)<<8)|((a[n+3]&0xff)<<0); n+=4;
page[i] = word;
}
if(length-n > 0) {
int word = memRead(addr);
- word = (word&0x00ffffff)|((a[n]&0xff)<<24);
- if(length-n > 1) { word = (word&0xff00ffff)|((a[n+1]&0xff)<<16); }
- if(length-n > 2) { word = (word&0xffff00ff)|((a[n+2]&0xff)<< 8); }
+ if(length-n >= 1) { word = (word&0x00ffffff)|((a[n+0]&0xff)<<24); }
+ if(length-n >= 2) { word = (word&0xff00ffff)|((a[n+1]&0xff)<<16); }
+ if(length-n >= 3) { word = (word&0xffff00ff)|((a[n+2]&0xff)<< 8); }
memWrite(addr,word);
}
}
+ /** Read a word from the processes memory at <i>addr</i> */
public final int memRead(int addr) throws ReadFaultException {
if((addr & 3) != 0) throw new ReadFaultException(addr);
int page = addr >>> PAGE_SHIFT;
} catch(ArrayIndexOutOfBoundsException e) {
if(page < 0) throw e; // should never happen
if(page > readPages.length) throw new ReadFaultException(addr);
- if(readPages[page] != emptyPage) throw e; // should never happen
+ if(readPages[page] != _emptyPage) throw e; // should never happen
initPage(page);
return 0;
} catch(NullPointerException e) {
}
}
- protected final void memWrite(int addr, int value) throws WriteFaultException {
+ /** Writes a word to the processes memory at <i>addr</i> */
+ public final void memWrite(int addr, int value) throws WriteFaultException {
if((addr & 3) != 0) throw new WriteFaultException(addr);
int page = addr >>> PAGE_SHIFT;
int entry = (addr>>>2)&(PAGE_WORDS-1);
} catch(ArrayIndexOutOfBoundsException e) {
if(page < 0) throw e;// should never happen
if(page > writePages.length) throw new WriteFaultException(addr);
- if(readPages[page] != emptyPage) throw e; // should never happen
+ if(readPages[page] != _emptyPage) throw e; // should never happen
initPage(page);
writePages[page][entry] = value;
} catch(NullPointerException e) {
}
}
- protected void initPage(int page) { writePages[page] = readPages[page] = new int[PAGE_WORDS]; }
+ /** Created a new non-empty writable page at page number <i>page</i> */
+ private final void initPage(int page) { initPage(page,false); }
+ /** Created a new non-empty page at page number <i>page</i>. If <i>ro</i> is set the page will be read-only */
+ private final void initPage(int page, boolean ro) {
+ int[] buf = new int[PAGE_WORDS];
+ writePages[page] = ro ? null : buf;
+ readPages[page] = buf;
+ }
+ /** Returns the exit status of the process. (only valid if state == DONE)
+ @see Runtime#state */
public final int exitStatus() {
if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state");
return exitStatus;
}
-
- public final int run(String[] args) throws EmulationException {
+
+ /** Runs the process until it exits and returns the exit status.
+ If the process executes the PAUSE syscall execution will be paused for 500ms and a warning will be displayed */
+ public final int run(String[] args) throws ExecutionException {
start(args);
for(;;) {
- execute();
- if(state != PAUSED) break;
+ if(execute()) break;
System.err.println("WARNING: Pause requested while executing run()");
try { Thread.sleep(500); } catch(InterruptedException e) { }
}
- if(state != DONE) throw new IllegalStateException("run() ended up in an inappropriate state");
return exitStatus();
}
- private void addArgs(String[] args) throws EmulationException {
- if(state == UNINITIALIZED || state == RUNNING || state == PAUSED) throw new IllegalStateException("addArgs() called in inappropriate state");
+ /** Adds the String[] array, <i>args</i>, to the arguments page in main memory */
+ private void addArgs(String[] args) throws ExecutionException {
int count = args.length;
byte[] nullTerminator = new byte[1];
int total = 4; /* null last table entry */
for(int i=0;i<count;i++) total += args[i].length() + 1/*null terminator*/ + 4/*table entry*/;
- if(total > PAGE_SIZE) throw new EmulationException("Arguments too large");
+ if(total >= PAGE_SIZE-4) throw new ExecutionException("Arguments too large");
int start = ARGS_ADDR;
int addr = start + (count+1)*4;
int[] table = new int[count+1];
for(int i=0;i<count;i++) {
byte[] a;
- try { a = args[i].getBytes("US-ASCII"); } catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
+ try { a = args[i].getBytes("US-ASCII"); } catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
table[i] = addr;
-
- copyout(a,addr,a.length);
- addr += a.length;
- copyout(nullTerminator,addr,1);
- addr += 1;
-
+ copyout(a,addr,a.length);
+ addr += a.length;
+ copyout(nullTerminator,addr,1);
+ addr += 1;
}
addr=start;
for(int i=0;i<count;i++) {
}
}
- public void setUserInfo(int index, int word) throws EmulationException {
- if(index < 0 || index >= 1024) throw new EmulationException("setUserInfo called with index >= 1024");
- memWrite(USER_INFO_ADDR+index*4,word);
+ /** Sets word number <i>index</i> in the _user_info table to <i>word</i>
+ _user_info is a 4096 byte table in the process's memory. It contains 1024 32-bit entries. This
+ can be used by the process to communicate with the caller of the process. Each entry is 32-bit
+ wide and can be used to store integers or pointers */
+ public void setUserInfo(int index, int word) {
+ if(index < 0 || index >= 1024) throw new IllegalStateException("setUserInfo called with index >= 1024");
+ try {
+ memWrite(USER_INFO_ADDR+index*4,word);
+ } catch(FaultException e) { throw new Error("should never happen: " + e); }
+ }
+
+ /** Returns the word in the _user_info table entry <i>index</i>
+ @see Runtime#setUserInfo(int,int) setUserInfo */
+ public int getUserInfo(int index) {
+ if(index < 0 || index >= 1024) throw new IllegalStateException("setUserInfo called with index >= 1024");
+ try {
+ return memRead(USER_INFO_ADDR+index*4);
+ } catch(FaultException e) { throw new Error("should never happen: " + e); }
}
- public int getUserInfo(int index) throws EmulationException {
- if(index < 0 || index >= 1024) throw new EmulationException("getUserInfo called with index >= 1024");
- return memRead(USER_INFO_ADDR+index*4);
+ /** Executes the process until the PAUSE syscall is invoked or the process exits. Returns true if the process exited. */
+ public final boolean execute() throws ExecutionException {
+ if(state == PAUSED) state = RUNNING;
+ if(state != RUNNING) throw new IllegalStateException("execute() called in inappropriate state");
+ _execute();
+ if(state != PAUSED && state != DONE) throw new IllegalStateException("execute() ended up in an inappropriate state");
+ return state == DONE;
}
- public final void start(String[] args) throws EmulationException {
- if(state == UNINITIALIZED || state == RUNNING || state == PAUSED) throw new IllegalStateException("start() called in inappropriate state");
+ /** Initializes the process and prepairs it to be executed with execute() */
+ public final void start(String[] args) throws ExecutionException {
+ if(state != INITIALIZED) throw new IllegalStateException("start() called in inappropriate state");
_start(entryPoint);
addArgs(args);
fds = new FileDescriptor[OPEN_MAX];
state = PAUSED;
}
+ /** Determines if the process can access <i>fileName</i>. The default implementation simply logs
+ the request and allows it */
+ protected boolean allowFileAccess(String fileName, boolean write) {
+ System.err.println("Allowing " + (write?"write":"read-only") + " to " + fileName);
+ return true;
+ }
+
+ /** Allocated an entry in the FileDescriptor table for <i>fd</i> and returns the number.
+ Returns -1 if the table is full. This can be used by subclasses to use custom file
+ descriptors */
+ protected int allocFDEnt(FileDescriptor fd) {
+ int i;
+ for(i=0;i<OPEN_MAX;i++) if(fds[i] == null) break;
+ if(i==OPEN_MAX) return -1;
+ fds[i] = fd;
+ return i;
+ }
+
+ /** The open syscall */
+ private int sys_open(int addr, int flags, int mode) {
+ final int O_RDONLY = 0;
+ final int O_WRONLY = 1;
+ final int O_RDWR = 2;
+ final int O_APPEND = 0x0008;
+ final int O_CREAT = 0x0200;
+ final int O_NONBLOCK = 0x4000;
+ final int O_EXCL = 0x0800;
- // Syscalls
- private int write(int fdn, int addr, int count) {
+ if((flags & O_APPEND) != 0) {
+ System.err.println("WARNING: O_APPEND not supported");
+ return -EOPNOTSUPP;
+ }
+ if((flags & O_NONBLOCK) != 0) {
+ System.err.println("WARNING: O_NONBLOCK not supported");
+ return -EOPNOTSUPP;
+ }
+
+ try {
+ String fileName = cstring(addr);
+ if(!allowFileAccess(fileName,(flags&3)!=0)) return -EACCES;
+
+ File f = new File(fileName);
+ if((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)
+ if(!f.createNewFile()) return -EEXIST;
+ if(f.exists()) {
+ if(f.length() >= Integer.MAX_VALUE) return -EOPNOTSUPP;
+ } else {
+ if((flags & O_CREAT) == 0) return -ENOENT;
+ }
+ int fdn = allocFDEnt(new RegularFileDescriptor(f,flags&3));
+ return fdn == -1 ? -ENFILE : fdn;
+ } catch(FaultException e) {
+ return -EFAULT;
+ } catch(FileNotFoundException e) {
+ if(e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
+ return -ENOENT;
+ } catch(IOException e) {
+ return -EIO;
+ }
+ }
+
+ /** The write syscall */
+ private int sys_write(int fdn, int addr, int count) {
int n = 0;
int r;
FileDescriptor fd;
}
}
- private int read(int fdn, int addr, int count) {
+ /** The read syscall */
+ private int sys_read(int fdn, int addr, int count) {
FileDescriptor fd;
count = Math.min(count,MAX_CHUNK);
try {
}
}
- private int close(int fdn) {
+ /** The close syscall */
+ private int sys_close(int fdn) {
FileDescriptor fd;
try {
fd = fds[fdn];
return 0;
}
+ /** The seek syscall */
+ private int sys_seek(int fdn, int offset, int whence) {
+ FileDescriptor fd;
+ try {
+ fd = fds[fdn];
+ if(fd == null || !fd.readable()) return -EBADFD;
+ } catch(ArrayIndexOutOfBoundsException e) {
+ return -EBADFD;
+ }
+ if(whence != FileDescriptor.SEEK_SET && whence != FileDescriptor.SEEK_CUR && whence != FileDescriptor.SEEK_END) return -EINVAL;
+ try {
+ int n = fd.seek(offset,whence);
+ return n < 0 ? -ESPIPE : n;
+ } catch(IOException e) {
+ return -ESPIPE;
+ }
+ }
+
+ /** The stat/fstat syscall helper */
private int stat(FileInfo fi, int addr) {
int size = fi.size();
try {
- memWrite(addr+0,0); // st_dev (top 16), // st_ino (bottom 16)
+ memWrite(addr+0,(1<<16)|1); // st_dev (top 16), // st_ino (bottom 16)
memWrite(addr+4,(fi.type() & 0xf000)|0644); // st_mode
- memWrite(addr+8,1); // st_nlink (top 16) // st_uid (bottom 16)
+ memWrite(addr+8,1<<16); // st_nlink (top 16) // st_uid (bottom 16)
memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16)
memWrite(addr+16,size); // st_size
memWrite(addr+20,0); // st_atime
// memWrite(addr+24,0) // st_spare1
memWrite(addr+28,(int)(fi.modTime()/1000)); // st_mtime
// memWrite(addr+32,0) // st_spare2
- memWrite(addr+36,0); // st_atime
+ memWrite(addr+36,0); // st_ctime
// memWrite(addr+40,0) // st_spare3
memWrite(addr+44,512); // st_bklsize;
memWrite(addr+48,(size+511)&(~511)); // st_blocks
return 0;
}
- private int fstat(int fdn, int addr) {
+ /** The fstat syscall */
+ private int sys_fstat(int fdn, int addr) {
FileDescriptor fd;
try {
fd = fds[fdn];
return stat(fd.fileInfo(),addr);
}
+ /** The sbrk syscall. This can also be used by subclasses to allocate memory.
+ <i>incr</i> is how much to increase the break by */
public int sbrk(int incr) {
if(incr==0) return brk<<PAGE_SHIFT;
int oldBrk = brk;
System.err.println("Hit BRK_LIMIT");
return -ENOMEM;
}
- for(int i=oldBrk;i<newBrk+256;i++)
- readPages[i] = writePages[i] = emptyPage;
+ for(int i=oldBrk;i<newBrk;i++)
+ readPages[i] = writePages[i] = emptyPage();
brk = newBrk;
return oldBrk<<PAGE_SHIFT;
}
- private int open(int addr, int flags, int mode) {
- final int O_RDONLY = 0;
- final int O_WRONLY = 1;
- final int O_RDWR = 2;
- final int O_APPEND = 0x0008;
- final int O_CREAT = 0x0200;
- final int O_NONBLOCK = 0x4000;
- final int O_EXCL = 0x0800;
-
- if((flags & O_APPEND) != 0) {
- System.err.println("WARNING: O_APPEND not supported");
- return -EOPNOTSUPP;
- }
- if((flags & O_NONBLOCK) != 0) {
- System.err.println("WARNING: O_NONBLOCK not supported");
- return -EOPNOTSUPP;
- }
-
- try {
- int fdn=-1;
- File f = new File(cstring(addr));
- System.err.println("Opening: " + f);
- if((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)
- if(!f.createNewFile()) return -EEXIST;
- if(f.exists()) {
- if(f.length() >= Integer.MAX_VALUE) return -EOPNOTSUPP;
- } else {
- if((flags & O_CREAT) == 0) return -ENOENT;
- }
- for(int i=0;i<OPEN_MAX;i++) if(fds[i] == null) { fdn = i; break; }
- if(fdn==-1) return -ENFILE;
- fds[fdn] = new RegularFileDescriptor(f,flags&3);
- return fdn;
- } catch(FaultException e) {
- return -EFAULT;
- } catch(FileNotFoundException e) {
- if(e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
- return -ENOENT;
- } catch(IOException e) {
- return -EIO;
- }
- }
-
- private int seek(int fdn, int offset, int whence) {
- FileDescriptor fd;
- try {
- fd = fds[fdn];
- if(fd == null || !fd.readable()) return -EBADFD;
- } catch(ArrayIndexOutOfBoundsException e) {
- return -EBADFD;
- }
- try {
- return fd.seek(offset,whence);
- } catch(IOException e) {
- System.err.println(e);
- return -EPIPE;
- }
- }
-
- // This will only be called by raise() to invoke the default handler
- // We don't have to worry about actually delivering the signal
- private int kill(int pid, int signal) {
+ /** The kill syscall.
+ SIGSTOP, SIGTSTO, SIGTTIN, and SIGTTOUT pause the process.
+ SIGCONT, SIGCHLD, SIGIO, and SIGWINCH are ignored.
+ Anything else terminates the process. */
+ private int sys_kill(int pid, int signal) {
+ // This will only be called by raise() in newlib to invoke the default handler
+ // We don't have to worry about actually delivering the signal
if(pid != PID) return -ESRCH;
if(signal < 0 || signal >= 32) return -EINVAL;
switch(signal) {
if(fds[2]==null) {
System.out.print(msg);
} else {
- byte[] b = msg.getBytes();
try {
+ byte[] b = msg.getBytes("US-ASCII");
fds[2].write(b,0,b.length);
- } catch(IOException e) { }
+ }
+ catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
+ catch(IOException e) { }
}
}
}
return 0;
}
- private int getpid() { return PID; }
+ /** The getpid syscall */
+ private int sys_getpid() { return PID; }
+ /** The syscall dispatcher.
+ The should be called by subclasses when the syscall instruction is invoked.
+ <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
+ the contenst of A0, A1, A2, and A3. The call MAY change the state
+ @see Runtime#state state */
protected int syscall(int syscall, int a, int b, int c, int d) {
switch(syscall) {
case SYS_null: return 0;
case SYS_exit: exitStatus = a; state = DONE; return 0;
case SYS_pause: state = PAUSED; return 0;
- case SYS_write: return write(a,b,c);
- case SYS_fstat: return fstat(a,b);
+ case SYS_write: return sys_write(a,b,c);
+ case SYS_fstat: return sys_fstat(a,b);
case SYS_sbrk: return sbrk(a);
- case SYS_open: return open(a,b,c);
- case SYS_close: return close(a);
- case SYS_read: return read(a,b,c);
- case SYS_seek: return seek(a,b,c);
- case SYS_kill: return kill(a,b);
- case SYS_getpid: return getpid();
+ case SYS_open: return sys_open(a,b,c);
+ case SYS_close: return sys_close(a);
+ case SYS_read: return sys_read(a,b,c);
+ case SYS_seek: return sys_seek(a,b,c);
+ case SYS_kill: return sys_kill(a,b);
+ case SYS_getpid: return sys_getpid();
default:
System.err.println("Attempted to use unknown syscall: " + syscall);
return -ENOSYS;
}
}
- // Helper function to read a cstring from main memory
+ /** Helper function to read a cstring from main memory */
public String cstring(int addr) throws ReadFaultException {
StringBuffer sb = new StringBuffer();
for(;;) {
}
}
}
-
- // Exceptions
- public static class ReadFaultException extends FaultException {
- public ReadFaultException(int addr) { super(addr); }
- }
- public static class WriteFaultException extends FaultException {
- public WriteFaultException(int addr) { super(addr); }
- }
- public static abstract class FaultException extends EmulationException {
- private int addr;
- public FaultException(int addr) { this.addr = addr; }
- public String getMessage() { return "fault at: " + toHex(addr); }
- }
- public static class EmulationException extends Exception {
- public EmulationException() { }
- public EmulationException(String s) { super(s); }
- }
- // FileInfo classes - used by stat(2)
- static class FileInfo {
+ /** FileInfo class - used by stat */
+ public static class FileInfo {
public static final int S_IFIFO = 0010000;
public static final int S_FCHR = 0020000;
public static final int S_IFDIR = 0040000;
public int type() { return S_IFIFO; }
public long modTime() { return 0; }
}
-
+
+ /** FileInfo subclass for normal files */
public static class FileFileInfo extends FileInfo {
public File f;
public FileFileInfo(File f) { this.f = f; }
public long modTime() { return f.lastModified(); }
}
- // File descriptor classes
+ /** File Descriptor class */
public static abstract class FileDescriptor {
+ protected final static int SEEK_SET = 0;
+ protected final static int SEEK_CUR = 1;
+ protected final static int SEEK_END = 2;
+
+ /** returns true if the fd is readable */
public boolean readable() { return false; }
+ /** returns true if the fd is writable */
public boolean writable() { return false; }
private static final FileInfo nullFi = new FileInfo();
private FileInfo fi;
public FileInfo fileInfo() { return fi; }
+ /** Initializes the FileDescriptor with no FileInfo struct */
FileDescriptor() { this(null); }
+ /** Initializes the FileDescriptor with the given FileInfo struct (used by fstat) */
FileDescriptor(FileInfo fi) { this.fi = fi==null ? nullFi : fi; }
+ /** Read some bytes. Should return the number of bytes read, 0 on EOF, or throw an IOException on error */
public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
+ /** Write. Should return the number of bytes written or throw an IOException on error */
public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
+
+ /** Seek in the filedescriptor. Whence is SEEK_SET, SEEK_CUR, or SEEK_END. Should return -1 on error or the new position. */
+ public int seek(int n, int whence) throws IOException { return -1; }
- public int seek(int n, int whence) throws IOException { return -ESPIPE; }
+ /** Should return true if this is a tty */
public boolean isatty() { return false; }
+ /** Closes the fd */
void close() { }
}
+ /** FileDescriptor class for normal files */
public static class RegularFileDescriptor extends FileDescriptor {
private int mode;
private RandomAccessFile raf;
if(raf.length() >= Integer.MAX_VALUE) throw new IOException("File too large");
}
- public int seek(int n, int whence) throws IOException {
- final int SEEK_SET = 0;
- final int SEEK_CUR = 1;
- final int SEEK_END = 2;
-
+ public int seek(int n_, int whence) throws IOException {
+ long n = n_;
switch(whence) {
case SEEK_SET: break;
- case SEEK_CUR: n = (int)(raf.getFilePointer()+n); break;
- case SEEK_END: n = (int)(raf.length()+n); break;
- default: return -EINVAL;
+ case SEEK_CUR: n += raf.getFilePointer(); break;
+ case SEEK_END: n += raf.length(); break;
+ default: return -1;
}
raf.seek(n);
- return n;
+ return (int)n;
}
public int write(byte[] a, int off, int length) throws IOException { raf.write(a,off,length); return length; }
public InputStreamFD(InputStream is) { this.is = is; }
public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
}
+
+ // Exceptions
+ public static class ReadFaultException extends FaultException {
+ public ReadFaultException(int addr) { super(addr); }
+ }
+ public static class WriteFaultException extends FaultException {
+ public WriteFaultException(int addr) { super(addr); }
+ }
+ public static abstract class FaultException extends ExecutionException {
+ private int addr;
+ public FaultException(int addr) { this.addr = addr; }
+ public String getMessage() { return "fault at: " + toHex(addr); }
+ }
+ public static class ExecutionException extends Exception {
+ public ExecutionException() { }
+ public ExecutionException(String s) { super(s); }
+ }
// Utility functions
private byte[] byteBuf(int size) {
if(_byteBuf==null) _byteBuf = new byte[size];
else if(_byteBuf.length < size)
- _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK+8)];
+ _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK)];
return _byteBuf;
}
+
protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
protected final static int min(int a, int b) { return a < b ? a : b; }
protected final static int max(int a, int b) { return a > b ? a : b; }
}
-
-
-
-interface Errno {
- public static final int EPERM = 1;
- public static final int ENOENT = 2;
- public static final int ESRCH = 3;
- public static final int EINTR = 4;
- public static final int EIO = 5;
- public static final int ENXIO = 6;
- public static final int ENOEXEC = 8;
- public static final int EBADF = 9;
- public static final int ECHILD = 10;
- public static final int EAGAIN = 11;
- public static final int ENOMEM = 12;
- public static final int EACCES = 13;
- public static final int EFAULT = 14;
- public static final int ENOTBLK = 15;
- public static final int EBUSY = 16;
- public static final int EEXIST = 17;
- public static final int EXDEV = 18;
- public static final int ENODEV = 19;
- public static final int ENOTDIR = 20;
- public static final int EISDIR = 21;
- public static final int EINVAL = 22;
- public static final int ENFILE = 23;
- public static final int EMFILE = 24;
- public static final int ENOTTY = 25;
- public static final int ETXTBSY = 26;
- public static final int EFBIG = 27;
- public static final int ENOSPC = 28;
- public static final int ESPIPE = 29;
- public static final int EROFS = 30;
- public static final int EMLINK = 31;
- public static final int EPIPE = 32;
- public static final int EDOM = 33;
- public static final int ERANGE = 34;
- public static final int ENOMSG = 35;
- public static final int EIDRM = 36;
- public static final int ECHRNG = 37;
- public static final int ELNRNG = 41;
- public static final int EUNATCH = 42;
- public static final int ENOCSI = 43;
- public static final int EDEADLK = 45;
- public static final int ENOLCK = 46;
- public static final int EBADE = 50;
- public static final int EBADR = 51;
- public static final int EXFULL = 52;
- public static final int ENOANO = 53;
- public static final int EBADRQC = 54;
- public static final int EBADSLT = 55;
- public static final int EDEADLOCK = 56;
- public static final int EBFONT = 57;
- public static final int ENOSTR = 60;
- public static final int ENODATA = 61;
- public static final int ETIME = 62;
- public static final int ENOSR = 63;
- public static final int ENONET = 64;
- public static final int ENOPKG = 65;
- public static final int EREMOTE = 66;
- public static final int ENOLINK = 67;
- public static final int EADV = 68;
- public static final int ESRMNT = 69;
- public static final int ECOMM = 70;
- public static final int EPROTO = 71;
- public static final int EMULTIHOP = 74;
- public static final int ELBIN = 75;
- public static final int EDOTDOT = 76;
- public static final int EBADMSG = 77;
- public static final int EFTYPE = 79;
- public static final int ENOTUNIQ = 80;
- public static final int EBADFD = 81;
- public static final int EREMCHG = 82;
- public static final int ELIBACC = 83;
- public static final int ELIBBAD = 84;
- public static final int ELIBSCN = 85;
- public static final int ELIBMAX = 86;
- public static final int ELIBEXEC = 87;
- public static final int ENOSYS = 88;
- public static final int ENMFILE = 89;
- public static final int ENOTEMPTY = 90;
- public static final int ENAMETOOLONG = 91;
- public static final int ELOOP = 92;
- public static final int EOPNOTSUPP = 95;
- public static final int EPFNOSUPPORT = 96;
- public static final int ECONNRESET = 104;
- public static final int ENOBUFS = 105;
- public static final int EAFNOSUPPORT = 106;
- public static final int EPROTOTYPE = 107;
- public static final int ENOTSOCK = 108;
- public static final int ENOPROTOOPT = 109;
- public static final int ESHUTDOWN = 110;
- public static final int ECONNREFUSED = 111;
- public static final int EADDRINUSE = 112;
- public static final int ECONNABORTED = 113;
- public static final int ENETUNREACH = 114;
- public static final int ENETDOWN = 115;
- public static final int ETIMEDOUT = 116;
- public static final int EHOSTDOWN = 117;
- public static final int EHOSTUNREACH = 118;
- public static final int EINPROGRESS = 119;
- public static final int EALREADY = 120;
- public static final int EDESTADDRREQ = 121;
- public static final int EMSGSIZE = 122;
- public static final int EPROTONOSUPPORT = 123;
- public static final int ESOCKTNOSUPPORT = 124;
- public static final int EADDRNOTAVAIL = 125;
- public static final int ENETRESET = 126;
- public static final int EISCONN = 127;
- public static final int ENOTCONN = 128;
- public static final int ETOOMANYREFS = 129;
- public static final int EPROCLIM = 130;
- public static final int EUSERS = 131;
- public static final int EDQUOT = 132;
- public static final int ESTALE = 133;
- public static final int ENOTSUP = 134;
- public static final int ENOMEDIUM = 135;
- public static final int ENOSHARE = 136;
- public static final int ECASECLASH = 137;
- public static final int EILSEQ = 138;
- public static final int EOVERFLOW = 139;
- public static final int __ELASTERROR = 2000;
-}
-
-interface Syscalls {
- public static final int SYS_null = 0;
- public static final int SYS_exit = 1;
- public static final int SYS_pause = 2;
- public static final int SYS_open = 3;
- public static final int SYS_close = 4;
- public static final int SYS_read = 5;
- public static final int SYS_write = 6;
- public static final int SYS_sbrk = 7;
- public static final int SYS_fstat = 8;
- public static final int SYS_isatty = 9;
- public static final int SYS_seek = 10;
- public static final int SYS_kill = 11;
- public static final int SYS_getpid = 12;
-}