2003/11/16 08:28:10
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:41:30 +0000 (07:41 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:41:30 +0000 (07:41 +0000)
darcs-hash:20040130074130-2ba56-e9919e297839a7213b832dd4750944a4334410c8.gz

19 files changed:
src/org/xwt/Picture.java
src/org/xwt/Platform.java
src/org/xwt/Res.java
src/org/xwt/Surface.java
src/org/xwt/Template.java
src/org/xwt/XWT.java
src/org/xwt/js/JSContext.java
src/org/xwt/js/JSFunction.java
src/org/xwt/js/JSTrap.java
src/org/xwt/mips/Compiler.java
src/org/xwt/mips/ELF.java
src/org/xwt/mips/Errno.java [new file with mode: 0644]
src/org/xwt/mips/Interpreter.java
src/org/xwt/mips/Registers.java [new file with mode: 0644]
src/org/xwt/mips/Runtime.java [moved from src/org/xwt/mips/VM.java with 51% similarity]
src/org/xwt/mips/Syscalls.java [new file with mode: 0644]
src/org/xwt/mips/linker.ld
src/org/xwt/translators/Freetype.java
src/org/xwt/translators/MSPack.java

index 3168390..13440f9 100644 (file)
@@ -33,25 +33,34 @@ public abstract class Picture {
     private static Cache cache = new Cache(100);
     private static GIF gif = new GIF();
     
+    public static class Holder {
+        public Picture picture = null;
+    }
+
     /** turns a resource into a Picture.Source and passes it to the callback */
-    public static Picture fromRes(final Res r, final Callback callback) {
-        Picture ret = (Picture)cache.get(r);
-        if (ret != null) return ret;
+    public static Holder fromRes(final Res r, final Scheduler.Task callback) {
+        Holder ret = (Holder)cache.get(r);
+        if (ret == null) {
+            ret = new Holder();
+            cache.put(r, ret);
+            if (callback == null) return null;
+        }
+        final Holder holder = ret;
         if (callback != null)
             new java.lang.Thread() { public void run() {
                 try {
                     final byte[] b = InputStreamToByteArray.convert(r.getInputStream());
                     Scheduler.add(new Scheduler.Task() { public void perform() {
                         try {
-                            Picture ret = null;
+                            Picture p = null;
                             InputStream pbis = new ByteArrayInputStream(b);
-                            if ((b[0] & 0xff) == 'G') ret = gif.fromInputStream(pbis, "some picture");
-                            else if ((b[0] & 0xff) == 137) ret = new PNG().fromInputStream(pbis, "some picture");
-                            else if ((b[0] & 0xff) == 0xff) ret = Platform.decodeJPEG(pbis, "some picture");
+                            if ((b[0] & 0xff) == 'G') p = gif.fromInputStream(pbis, "some picture");
+                            else if ((b[0] & 0xff) == 137) p = new PNG().fromInputStream(pbis, "some picture");
+                            else if ((b[0] & 0xff) == 0xff) p = Platform.decodeJPEG(pbis, "some picture");
                             else throw new JS.Exn("couldn't figure out image type from first byte");
-                            ret.res = r;
-                            cache.put(r, ret);
-                            callback.call(ret);
+                            p.res = r;
+                            holder.picture = p;
+                            Scheduler.add(callback);
                         } catch (Exception e) {
                             Log.log(Picture.class, e);
                         } } });
@@ -60,6 +69,6 @@ public abstract class Picture {
                     return;
                 }
             } }.start();
-        return null;
+        return ret;
     }
 }
index 9c1d429..00b00d2 100644 (file)
@@ -132,16 +132,6 @@ public class Platform {
         return _createPicture(b2,w,h);
     }
 
-    /** creates a socket object */
-    public static Socket getSocket(String host, int port, boolean ssl, boolean negotiate) throws IOException {
-        return platform._getSocket(host, port, ssl, negotiate);
-    }
-    protected Socket _getSocket(String host, int port, boolean ssl, boolean negotiate) throws IOException {
-        Socket ret = ssl ? new TinySSL(host, port, negotiate) : new Socket(java.net.InetAddress.getByName(host), port);
-        ret.setTcpNoDelay(true);
-        return ret;
-    }
-    
     /** should return true if it is safe to supress full-surface dirties immediately after a window resize */
     public static boolean supressDirtyOnResize() { return platform._supressDirtyOnResize(); }
     protected boolean _supressDirtyOnResize() { return false; }
@@ -166,10 +156,6 @@ public class Platform {
     public static int getScreenHeight() { return platform._getScreenHeight(); }
     protected int _getScreenHeight() { return 480; }
 
-    /** returns the maximum number of threads that the XWT engine can create without adversely affecting the host OS */
-    public static int maxThreads() { return platform._maxThreads(); }
-    protected int _maxThreads() { return 25; }
-
     /** used to notify the user of very serious failures; usually used when logging is not working or unavailable */
     protected void _criticalAbort(String message) { System.exit(-1); }
 
index 53da6ff..e9a9465 100644 (file)
@@ -12,19 +12,6 @@ import org.bouncycastle.util.encoders.Base64;
 /** Base class for XWT resources */
 public abstract class Res extends JS {
 
-
-    // Public Static //////////////////////////////////////////////////////////////////////
-
-    // FIXME: move to XWT.load()?
-    public static Res fromString(String url) {
-        if (url.startsWith("http://")) return new HTTP(url);
-        else if (url.startsWith("https://")) return new HTTP(url);
-        else if (url.startsWith("data:")) return new ByteArray(Base64.decode(url.substring(5)), null);
-        else if (url.startsWith("utf8:")) return new ByteArray(url.substring(5).getBytes(), null);
-        throw new JS.Exn("invalid resource specifier " + url);
-    }
-
-
     // Base Class //////////////////////////////////////////////////////////////////////
 
     public String typeName() { return "resource"; }
index 36c82ee..3a083cc 100644 (file)
@@ -24,13 +24,8 @@ public abstract class Surface extends PixelBuffer {
         
     // Static Data ////////////////////////////////////////////////////////////////////////////////
 
-    private abstract static class Message extends Scheduler.Task {
-        public abstract void perform();
-        public Object call(Object arg) { perform(); return null; }
-    }
-
     /**< the most recently enqueued Move message; used to throttle the message rate */
-    private static Message lastMoveMessage = null;
+    private static Scheduler.Task lastMoveMessage = null;
 
     /** all instances of Surface which need to be refreshed by the Scheduler */
     public static Vec allSurfaces = new Vec();
@@ -89,9 +84,7 @@ public abstract class Surface extends PixelBuffer {
     private int platform_window_height = 0;
     protected final void setSize(int width, int height) {
         if (root.width != width || root.height != height) {
-            /*
             root.dirty(0, root.height - Main.scarImage.getHeight(), Main.scarImage.getWidth(), Main.scarImage.getHeight());
-            */
             root.width = Math.max(Main.scarImage.getWidth(), width);
             root.height = Math.max(Main.scarImage.getHeight(), height);
         }
@@ -113,7 +106,7 @@ public abstract class Surface extends PixelBuffer {
         else if (button == 2) new SimpleMessage("Press2", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
         else if (button == 3) {
             final Box who = Box.whoIs(root, mousex, mousey);
-            Scheduler.add(new Message() { public void perform() {
+            Scheduler.add(new Scheduler.Task() { public void perform() {
                 Platform.clipboardReadEnabled = true;
                 root.putAndTriggerJSTraps("Press3", Boolean.TRUE);
                 Platform.clipboardReadEnabled = false;
@@ -153,7 +146,9 @@ public abstract class Surface extends PixelBuffer {
         else if (button == 3) new SimpleMessage("DoubleClick3", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
     }
 
-    /** sends a KeyPressed message; subclasses should not add the C- or A- prefixes, nor should they capitalize alphabet characters */
+    /** Sends a KeyPressed message; subclasses should not add the C- or A- prefixes,
+     *  nor should they capitalize alphabet characters
+     */
     protected final void KeyPressed(String key) {
         if (key == null) return;
 
@@ -171,7 +166,7 @@ public abstract class Surface extends PixelBuffer {
     }
 
     // This is implemented as a private static class instead of an anonymous class to work around a GCJ bug
-    private class KMessage extends Message {
+    private class KMessage extends Scheduler.Task {
         String key = null;
         public KMessage(String k) { key = k; }
         public void perform() {
@@ -188,13 +183,14 @@ public abstract class Surface extends PixelBuffer {
 
     Vec keywatchers = new Vec();
 
-    /** sends a KeyReleased message; subclasses should not add the C- or A- prefixes, nor should they capitalize alphabet characters */
+    /** sends a KeyReleased message; subclasses should not add the C- or A- prefixes,
+     *  nor should they capitalize alphabet characters */
     protected final void KeyReleased(final String key) {
         if (key == null) return;
         if (key.toLowerCase().equals("alt")) alt = false;
         else if (key.toLowerCase().equals("control")) control = false;
         else if (key.toLowerCase().equals("shift")) shift = false;
-        Scheduler.add(new Message() { public void perform() {
+        Scheduler.add(new Scheduler.Task() { public void perform() {
             outer: for(int i=0; i<keywatchers.size(); i++) {
                 Box b = (Box)keywatchers.elementAt(i);
                 for(Box cur = b; cur != null; cur = cur.parent)
@@ -211,7 +207,7 @@ public abstract class Surface extends PixelBuffer {
      *  message), the subclass should use (-1,-1).
      */
     protected final void Move(final int newmousex, final int newmousey) {
-        Scheduler.add(lastMoveMessage = new Message() { public void perform() {
+        Scheduler.add(lastMoveMessage = new Scheduler.Task() { public void perform() {
             synchronized(Surface.this) {
 
                 // if move messages are arriving faster than we can process them, we just start ignoring them
@@ -236,7 +232,7 @@ public abstract class Surface extends PixelBuffer {
     }
 
     protected final void SizeChange(final int width, final int height) {
-        Scheduler.add(new Message() { public void perform() {
+        Scheduler.add(new Scheduler.Task() { public void perform() {
             if (width == root.width && height == root.height) return;
             root.set(root.REFLOW);
             platform_window_width = width;
@@ -247,7 +243,7 @@ public abstract class Surface extends PixelBuffer {
     }
 
     protected final void PosChange(final int x, final int y) {
-        Scheduler.add(new Message() { public void perform() {
+        Scheduler.add(new Scheduler.Task() { public void perform() {
             root.x = x;
             root.y = y;
             root.putAndTriggerJSTraps("PosChange", Boolean.TRUE);
@@ -362,7 +358,7 @@ public abstract class Surface extends PixelBuffer {
     }
 
     // FEATURE: reinstate recycler
-    public class SimpleMessage extends Message {
+    public class SimpleMessage extends Scheduler.Task {
         
         private Box boxContainingMouse;
         private Object value;
index c45134c..35de96b 100644 (file)
@@ -70,7 +70,8 @@ public class Template {
     public static Res resolveStringToResource(String str, XWT xwt, boolean permitAbsolute) {
         // URL
         if (str.indexOf("://") != -1) {
-            if (permitAbsolute) return Res.fromString(str);
+            // FIXME
+            //if (permitAbsolute) return Res.fromString(str);
             Log.log(Template.class, "absolute URL " + str + " not permitted here");
             return null;
         }
index e14d386..811a643 100644 (file)
@@ -59,8 +59,7 @@ public final class XWT extends JSCallable {
         case "undocumented": return new Sub("undocumented");
         case "undocumented.internal": return new Sub("undocumented.internal");
         //#end
-
-            return null;
+        return null;
     }
 
     public Object get(Object name) {
@@ -117,7 +116,14 @@ public final class XWT extends JSCallable {
             case "res.uncab": return new Res.Cab((Res)a);
             case "res.cache": try { return new Res.CachedRes((Res)a, "resources", true); }
                               catch (Res.NotCacheableException e) { throw new JS.Exn("this resource cannot be cached"); }
-            case "res.url": return Res.fromString((String)a);
+            case "res.url":
+                String url = (String)a;
+                if (url.startsWith("http://")) return new Res.HTTP(url);
+                else if (url.startsWith("https://")) return new Res.HTTP(url);
+                else if (url.startsWith("data:")) return new Res.ByteArray(Base64.decode(url.substring(5)), null);
+                else if (url.startsWith("utf8:")) return new Res.ByteArray(url.substring(5).getBytes(), null);
+                throw new JS.Exn("invalid resource specifier " + url);
+
             case "thread.sleep": sleep(JS.toInt(a)); return null;
             case "log.println": Log.logJS(this, a== null ? "**null**" : a.toString()); return null;
             case "log.dump": Log.recursiveLog("","",a); return null;
index e707326..ec73e94 100644 (file)
@@ -30,7 +30,21 @@ public class JSContext {
     Vec stack = new Vec();        ///< the object stack
     int pc = 0;                   ///< the program counter
 
-    public static void invokePauseable(JSFunction function) { new JSContext(function, true).invoke(new JSArray()); }
+    /** can be paused */
+    public static void invokePauseable(JSFunction function) {
+        new JSContext(function, true).invoke(new JSArray());
+    }
+
+    /** cannot be paused */
+    public static void invokeTrap(JSTrap.JSTrappable t, Object key, Object value) {
+        JSFunction invoker = new JSFunction("trap invoker", 0, null);
+        invoker.add(-1, ByteCodes.PUT, null);
+        JSContext cx = new JSContext(invoker, false);
+        cx.stack.push(t);
+        cx.stack.push(key);
+        cx.stack.push(value);
+        cx.resume();
+    }
 
     JSContext(JSFunction f, boolean pauseable) {
         this.pauseable = pauseable;
@@ -41,7 +55,7 @@ public class JSContext {
     void invoke(JSArray args) {
         JSFunction tf = f;
         f = null;
-        stack.push(new JSFunction.CallMarker(this));
+        stack.push(new Interpreter.CallMarker(this));
         f = tf;
         stack.push(args);
         resume();
@@ -65,7 +79,7 @@ public class JSContext {
         JSContext old = (JSContext)threadToJSContext.get(t);
         threadToJSContext.put(t, this);
         try {
-            JSFunction.eval(this);
+            Interpreter.eval(this);
         } finally {
             if (old == null) threadToJSContext.remove(t);
             else threadToJSContext.put(t, old);
index 00244c6..55741a5 100644 (file)
@@ -8,13 +8,6 @@ import java.io.*;
 /** A JavaScript function, compiled into bytecode */
 public class JSFunction extends JSCallable implements ByteCodes, Tokens {
 
-    /** Note: code gets run in an <i>unpauseable</i> context. */
-    public Object call(JSArray args) {
-        JSContext cx = new JSContext(this, false);
-        cx.invoke(args);
-        return cx.stack.pop();
-    }
-
 
     // Fields and Accessors ///////////////////////////////////////////////
 
@@ -42,7 +35,7 @@ public class JSFunction extends JSCallable implements ByteCodes, Tokens {
         return ret;
     }
 
-    private JSFunction(String sourceName, int firstLine, JSScope parentJSScope) {
+    JSFunction(String sourceName, int firstLine, JSScope parentJSScope) {
         this.sourceName = sourceName;
         this.firstLine = firstLine;
         this.parentJSScope = parentJSScope;
@@ -60,7 +53,13 @@ public class JSFunction extends JSCallable implements ByteCodes, Tokens {
         add(-1, LITERAL, null); 
         add(-1, RETURN);
     }
-    
+
+    /** Note: code gets run in an <i>unpauseable</i> context. */
+    public Object call(JSArray args) {
+        JSContext cx = new JSContext(this, false);
+        cx.invoke(args);
+        return cx.stack.pop();
+    }
 
 
     // Adding and Altering Bytecodes ///////////////////////////////////////////////////
@@ -86,451 +85,6 @@ public class JSFunction extends JSCallable implements ByteCodes, Tokens {
     }
     
 
-    // Invoking the Bytecode ///////////////////////////////////////////////////////
-
-    /** returns false if the thread has been paused */
-    static Object eval(final JSContext cx) throws JS.Exn {
-        final int initialPauseCount = cx.pausecount;
-        OUTER: for(;; cx.pc++) {
-        try {
-            if (cx.f == null || cx.pc >= cx.f.size) return cx.stack.pop();
-            int op = cx.f.op[cx.pc];
-            Object arg = cx.f.arg[cx.pc];
-            if(op == FINALLY_DONE) {
-                FinallyData fd = (FinallyData) cx.stack.pop();
-                if(fd == null) continue OUTER; // NOP
-                op = fd.op;
-                arg = fd.arg;
-            }
-            switch(op) {
-            case LITERAL: cx.stack.push(arg); break;
-            case OBJECT: cx.stack.push(new JSObj()); break;
-            case ARRAY: cx.stack.push(new JSArray(JS.toNumber(arg).intValue())); break;
-            case DECLARE: cx.scope.declare((String)(arg==null ? cx.stack.peek() : arg)); if(arg != null) cx.stack.push(arg); break;
-            case TOPSCOPE: cx.stack.push(cx.scope); break;
-            case JT: if (JS.toBoolean(cx.stack.pop())) cx.pc += JS.toNumber(arg).intValue() - 1; break;
-            case JF: if (!JS.toBoolean(cx.stack.pop())) cx.pc += JS.toNumber(arg).intValue() - 1; break;
-            case JMP: cx.pc += JS.toNumber(arg).intValue() - 1; break;
-            case POP: cx.stack.pop(); break;
-            case SWAP: {
-                int depth = (arg == null ? 1 : toInt(arg));
-                Object save = cx.stack.elementAt(cx.stack.size() - 1);
-                for(int i=cx.stack.size() - 1; i > cx.stack.size() - 1 - depth; i--)
-                    cx.stack.setElementAt(cx.stack.elementAt(i-1), i);
-                cx.stack.setElementAt(save, cx.stack.size() - depth - 1);
-                break; }
-            case DUP: cx.stack.push(cx.stack.peek()); break;
-            case NEWSCOPE: cx.scope = new JSScope(cx.scope); break;
-            case OLDSCOPE: cx.scope = cx.scope.getParentJSScope(); break;
-            case ASSERT: if (!JS.toBoolean(cx.stack.pop())) throw je("assertion failed"); break;
-            case BITNOT: cx.stack.push(new Long(~JS.toLong(cx.stack.pop()))); break;
-            case BANG: cx.stack.push(new Boolean(!JS.toBoolean(cx.stack.pop()))); break;
-            case NEWFUNCTION: cx.stack.push(((JSFunction)arg).cloneWithNewParentJSScope(cx.scope)); break;
-            case LABEL: break;
-
-            case TYPEOF: {
-                Object o = cx.stack.pop();
-                if (o == null) cx.stack.push(null);
-                else if (o instanceof JS) cx.stack.push(((JS)o).typeName());
-                else if (o instanceof String) cx.stack.push("string");
-                else if (o instanceof Number) cx.stack.push("number");
-                else if (o instanceof Boolean) cx.stack.push("boolean");
-                else cx.stack.push("unknown");
-                break;
-            }
-
-            case PUSHKEYS: {
-                Object o = cx.stack.peek();
-                Enumeration e = ((JS)o).keys();
-                JSArray a = new JSArray();
-                while(e.hasMoreElements()) a.addElement(e.nextElement());
-                cx.stack.push(a);
-                break;
-            }
-
-            case LOOP:
-                cx.stack.push(new LoopMarker(cx.pc, cx.pc > 0 && cx.f.op[cx.pc - 1] == LABEL ?
-                                             (String)cx.f.arg[cx.pc - 1] : (String)null, cx.scope));
-                cx.stack.push(Boolean.TRUE);
-                break;
-
-            case BREAK:
-            case CONTINUE:
-                while(cx.stack.size() > 0) {
-                    Object o = cx.stack.pop();
-                    if (o instanceof CallMarker) ee("break or continue not within a loop");
-                    if (o instanceof TryMarker) {
-                        if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going
-                        cx.stack.push(new FinallyData(op, arg));
-                        cx.scope = ((TryMarker)o).scope;
-                        cx.pc = ((TryMarker)o).finallyLoc - 1;
-                        continue OUTER;
-                    }
-                    if (o instanceof LoopMarker) {
-                        if (arg == null || arg.equals(((LoopMarker)o).label)) {
-                            int loopInstructionLocation = ((LoopMarker)o).location;
-                            int endOfLoop = ((Integer)cx.f.arg[loopInstructionLocation]).intValue() + loopInstructionLocation;
-                            cx.scope = ((LoopMarker)o).scope;
-                            if (op == CONTINUE) { cx.stack.push(o); cx.stack.push(Boolean.FALSE); }
-                            cx.pc = op == BREAK ? endOfLoop - 1 : loopInstructionLocation;
-                            continue OUTER;
-                        }
-                    }
-                }
-                throw new Error("CONTINUE/BREAK invoked but couldn't find LoopMarker at " +
-                                cx.getSourceName() + ":" + cx.getLine());
-
-            case TRY: {
-                int[] jmps = (int[]) arg;
-                // jmps[0] is how far away the catch block is, jmps[1] is how far away the finally block is
-                // each can be < 0 if the specified block does not exist
-                cx.stack.push(new TryMarker(jmps[0] < 0 ? -1 : cx.pc + jmps[0], jmps[1] < 0 ? -1 : cx.pc + jmps[1], cx.scope));
-                break;
-            }
-
-            case RETURN: {
-                Object retval = cx.stack.pop();
-                while(cx.stack.size() > 0) {
-                    Object o = cx.stack.pop();
-                    if (o instanceof TryMarker) {
-                        if(((TryMarker)o).finallyLoc < 0) continue;
-                        cx.stack.push(retval); 
-                        cx.stack.push(new FinallyData(RETURN));
-                        cx.scope = ((TryMarker)o).scope;
-                        cx.pc = ((TryMarker)o).finallyLoc - 1;
-                        continue OUTER;
-                    } else if (o instanceof CallMarker) {
-                        if (cx.scope instanceof JSTrap.JSTrapScope) {
-                            JSTrap.JSTrapScope ts = (JSTrap.JSTrapScope)cx.scope;
-                            if (!ts.cascadeHappened) {
-                                ts.cascadeHappened = true;
-                                JSTrap t = ts.t.next;
-                                while (t != null && t.f.numFormalArgs == 0) t = t.next;
-                                if (t == null) {
-                                    ((JS)ts.t.trapee).put(t.name, ts.val);
-                                    if (cx.pausecount > initialPauseCount) return null;   // we were paused
-                                } else {
-                                    cx.stack.push(o);
-                                    JSArray args = new JSArray();
-                                    args.addElement(ts.val);
-                                    cx.stack.push(args);
-                                    cx.f = t.f;
-                                    cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, t, ts.val);
-                                    cx.pc = -1;
-                                    break;
-                                }
-                            }
-                        }
-                        cx.scope = ((CallMarker)o).scope;
-                        cx.pc = ((CallMarker)o).pc;
-                        cx.f = (JSFunction)((CallMarker)o).f;
-                        cx.stack.push(retval);
-                        continue OUTER;
-                    }
-                }
-                throw new Error("error: RETURN invoked but couldn't find a CallMarker!");
-            }
-
-            case PUT: {
-                Object val = cx.stack.pop();
-                Object key = cx.stack.pop();
-                Object target = cx.stack.peek();
-                if (target == null)
-                    throw je("tried to put a value to the " + key + " property on the null value");
-                if (!(target instanceof JS))
-                    throw je("tried to put a value to the " + key + " property on a " + target.getClass().getName());
-                if (key == null)
-                    throw je("tried to assign \"" + (val==null?"(null)":val.toString()) + "\" to the null key");
-                JSTrap t = null;
-                if (target instanceof JSTrap.JSTrappable) {
-                    t = ((JSTrap.JSTrappable)target).getTrap(val);
-                    while (t != null && t.f.numFormalArgs == 0) t = t.next;
-                } else if (target instanceof JSTrap.JSTrapScope && key.equals("cascade")) {
-                    JSTrap.JSTrapScope ts = (JSTrap.JSTrapScope)target;
-                    t = ts.t.next;
-                    ts.cascadeHappened = true;
-                    while (t != null && t.f.numFormalArgs == 0) t = t.next;
-                    if (t == null) target = ts.t.trapee;
-                }
-                if (t != null) {
-                    cx.stack.push(new CallMarker(cx));
-                    JSArray args = new JSArray();
-                    args.addElement(val);
-                    cx.stack.push(args);
-                    cx.f = t.f;
-                    cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, t, val);
-                    cx.pc = -1;
-                    break;
-                }
-                ((JS)target).put(key, val);
-                if (cx.pausecount > initialPauseCount) return null;   // we were paused
-                cx.stack.push(val);
-                break;
-            }
-
-            case GET:
-            case GET_PRESERVE: {
-                Object o, v;
-                if (op == GET) {
-                    v = arg == null ? cx.stack.pop() : arg;
-                    o = cx.stack.pop();
-                } else {
-                    v = cx.stack.pop();
-                    o = cx.stack.peek();
-                    cx.stack.push(v);
-                }
-                Object ret = null;
-                if (v == null) throw je("tried to get the null key from " + o);
-                if (o == null) throw je("tried to get property \"" + v + "\" from the null value @" + cx.pc + "\n" + cx.f.dump());
-                if (o instanceof String || o instanceof Number || o instanceof Boolean) {
-                    ret = Internal.getFromPrimitive(o,v);
-                    cx.stack.push(ret);
-                    break;
-                } else if (o instanceof JS) {
-                    JSTrap t = null;
-                    if (o instanceof JSTrap.JSTrappable) {
-                        t = ((JSTrap.JSTrappable)o).getTrap(v);
-                        while (t != null && t.f.numFormalArgs != 0) t = t.next;
-                    } else if (o instanceof JSTrap.JSTrapScope && v.equals("cascade")) {
-                        t = ((JSTrap.JSTrapScope)o).t.next;
-                        while (t != null && t.f.numFormalArgs != 0) t = t.next;
-                        if (t == null) o = ((JSTrap.JSTrapScope)o).t.trapee;
-                    }
-                    if (t != null) {
-                        cx.stack.push(new CallMarker(cx));
-                        JSArray args = new JSArray();
-                        cx.stack.push(args);
-                        cx.f = t.f;
-                        cx.scope = new JSTrap.JSTrapScope(cx.f.parentJSScope, t, null);
-                        ((JSTrap.JSTrapScope)cx.scope).cascadeHappened = true;
-                        cx.pc = -1;
-                        break;
-                    }
-                    ret = ((JS)o).get(v);
-                    if (cx.pausecount > initialPauseCount) return null;   // we were paused
-                    cx.stack.push(ret);
-                    break;
-                }
-                throw je("tried to get property " + v + " from a " + o.getClass().getName());
-            }
-            
-            case CALL: case CALLMETHOD: {
-                int numArgs = JS.toInt(arg);
-                Object method = null;
-                Object object = null;
-                Object ret = null;
-                JSArray arguments = null;
-
-                if(op == CALLMETHOD) {
-                    Object getResult = cx.stack.pop();
-                    method = cx.stack.pop();
-                    object = cx.stack.pop();
-                    if (getResult != null) {
-                        method = null;
-                        object = getResult;
-                    }
-                } else {
-                    method = null;
-                    object = cx.stack.pop();
-                }
-
-                if (object instanceof String || object instanceof Number || object instanceof Boolean) {
-                    arguments = new JSArray(); for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j);
-                    ret = Internal.callMethodOnPrimitive(object, method, arguments);
-
-                } else if (object instanceof JSFunction) {
-                    // FEATURE: use something similar to call0/call1/call2 here
-                    arguments = new JSArray(); for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j);
-                    cx.stack.push(new CallMarker(cx));
-                    cx.stack.push(arguments);
-                    cx.f = (JSFunction)object;
-                    cx.scope = new JSScope(cx.f.parentJSScope);
-                    cx.pc = -1;
-                    break;
-
-                } else if (object instanceof JSCallable) {
-                    JSCallable c = (JSCallable)object;
-                    switch(numArgs) {
-                        case 0: ret = c.call0(method); break;
-                        case 1: ret = c.call1(method, cx.stack.pop()); break;
-                        case 2: {
-                            Object first = cx.stack.pop();
-                            Object second = cx.stack.pop();
-                            ret = c.call2(method, second, first);
-                            break;
-                        }
-                        default: {
-                            arguments = new JSArray();
-                            for(int j=numArgs - 1; j >= 0; j--) arguments.setElementAt(cx.stack.pop(), j);
-                            ret = c.call(method, arguments);
-                            break;
-                        }
-                    }
-                } else {
-                    throw new JS.Exn("can't call a " + object.getClass().getName() + " @" + cx.pc + "\n" + cx.f.dump());
-                }
-                if (cx.pausecount > initialPauseCount) return null;
-                cx.stack.push(ret);
-                break;
-            }
-
-            case THROW: {
-                Object o = cx.stack.pop();
-                if(o instanceof JS.Exn) throw (JS.Exn)o;
-                throw new JS.Exn(o);
-            }
-
-            case ASSIGN_SUB: case ASSIGN_ADD: {
-                Object val = cx.stack.pop();
-                Object old = cx.stack.pop();
-                Object key = cx.stack.pop();
-                Object obj = cx.stack.peek();
-                if (val instanceof JSFunction && obj instanceof JSScope) {
-                    JSScope parent = (JSScope)obj;
-                    while(parent.getParentJSScope() != null) parent = parent.getParentJSScope();
-                    if (parent instanceof JSTrap.JSTrappable) {
-                        JSTrap.JSTrappable b = (JSTrap.JSTrappable)parent;
-                        if (op == ASSIGN_ADD) JSTrap.addTrap(b, key, (JSFunction)val);
-                        else JSTrap.delTrap(b, key, (JSFunction)val);
-                        // skip over the "normal" implementation of +=/-=
-                        cx.pc += ((Integer)arg).intValue() - 1;
-                        break;
-                    }
-                }
-                // use the "normal" implementation
-                cx.stack.push(key);
-                cx.stack.push(old);
-                cx.stack.push(val);
-                break;
-            }
-
-            case ADD: {
-                int count = ((Number)arg).intValue();
-                if(count < 2) throw new Error("this should never happen");
-                if(count == 2) {
-                    // common case
-                    Object right = cx.stack.pop();
-                    Object left = cx.stack.pop();
-                    if(left instanceof String || right instanceof String)
-                        cx.stack.push(JS.toString(left).concat(JS.toString(right)));
-                    else cx.stack.push(new Double(JS.toDouble(left) + JS.toDouble(right)));
-                } else {
-                    Object[] args = new Object[count];
-                    while(--count >= 0) args[count] = cx.stack.pop();
-                    if(args[0] instanceof String) {
-                        StringBuffer sb = new StringBuffer(64);
-                        for(int i=0;i<args.length;i++) sb.append(JS.toString(args[i]));
-                        cx.stack.push(sb.toString());
-                    } else {
-                        int numStrings = 0;
-                        for(int i=0;i<args.length;i++) if(args[i] instanceof String) numStrings++;
-                        if(numStrings == 0) {
-                            double d = 0.0;
-                            for(int i=0;i<args.length;i++) d += JS.toDouble(args[i]);
-                            cx.stack.push(new Double(d));
-                        } else {
-                            int i=0;
-                            StringBuffer sb = new StringBuffer(64);
-                            if(!(args[0] instanceof String || args[1] instanceof String)) {
-                                double d=0.0;
-                                do {
-                                    d += JS.toDouble(args[i++]);
-                                } while(!(args[i] instanceof String));
-                                sb.append(JS.toString(new Double(d)));
-                            }
-                            while(i < args.length) sb.append(JS.toString(args[i++]));
-                            cx.stack.push(sb.toString());
-                        }
-                    }
-                }
-                break;
-            }
-
-            default: {
-                Object right = cx.stack.pop();
-                Object left = cx.stack.pop();
-                switch(op) {
-                        
-                case BITOR: cx.stack.push(new Long(JS.toLong(left) | JS.toLong(right))); break;
-                case BITXOR: cx.stack.push(new Long(JS.toLong(left) ^ JS.toLong(right))); break;
-                case BITAND: cx.stack.push(new Long(JS.toLong(left) & JS.toLong(right))); break;
-
-                case SUB: cx.stack.push(new Double(JS.toDouble(left) - JS.toDouble(right))); break;
-                case MUL: cx.stack.push(new Double(JS.toDouble(left) * JS.toDouble(right))); break;
-                case DIV: cx.stack.push(new Double(JS.toDouble(left) / JS.toDouble(right))); break;
-                case MOD: cx.stack.push(new Double(JS.toDouble(left) % JS.toDouble(right))); break;
-                        
-                case LSH: cx.stack.push(new Long(JS.toLong(left) << JS.toLong(right))); break;
-                case RSH: cx.stack.push(new Long(JS.toLong(left) >> JS.toLong(right))); break;
-                case URSH: cx.stack.push(new Long(JS.toLong(left) >>> JS.toLong(right))); break;
-                        
-                case LT: case LE: case GT: case GE: {
-                    if (left == null) left = new Integer(0);
-                    if (right == null) right = new Integer(0);
-                    int result = 0;
-                    if (left instanceof String || right instanceof String) {
-                        result = left.toString().compareTo(right.toString());
-                    } else {
-                        result = (int)java.lang.Math.ceil(JS.toDouble(left) - JS.toDouble(right));
-                    }
-                    cx.stack.push(new Boolean((op == LT && result < 0) || (op == LE && result <= 0) ||
-                                       (op == GT && result > 0) || (op == GE && result >= 0)));
-                    break;
-                }
-                    
-                case EQ:
-                case NE: {
-                    Object l = left;
-                    Object r = right;
-                    boolean ret;
-                    if (l == null) { Object tmp = r; r = l; l = tmp; }
-                    if (l == null && r == null) ret = true;
-                    else if (r == null) ret = false; // l != null, so its false
-                    else if (l instanceof Boolean) ret = new Boolean(JS.toBoolean(r)).equals(l);
-                    else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue();
-                    else if (l instanceof String) ret = r != null && l.equals(r.toString());
-                    else ret = l.equals(r);
-                    cx.stack.push(new Boolean(op == EQ ? ret : !ret)); break;
-                }
-
-                default: throw new Error("unknown opcode " + op);
-                } }
-            }
-
-        } catch(JS.Exn e) {
-            while(cx.stack.size() > 0) {
-                Object o = cx.stack.pop();
-                if (o instanceof CatchMarker || o instanceof TryMarker) {
-                    boolean inCatch = o instanceof CatchMarker;
-                    if(inCatch) {
-                        o = cx.stack.pop();
-                        if(((TryMarker)o).finallyLoc < 0) continue; // no finally block, keep going
-                    }
-                    if(!inCatch && ((TryMarker)o).catchLoc >= 0) {
-                        // run the catch block, this will implicitly run the finally block, if it exists
-                        cx.stack.push(o);
-                        cx.stack.push(catchMarker);
-                        cx.stack.push(e.getObject());
-                        cx.scope = ((TryMarker)o).scope;
-                        cx.pc = ((TryMarker)o).catchLoc - 1;
-                        continue OUTER;
-                    } else {
-                        cx.stack.push(e);
-                        cx.stack.push(new FinallyData(THROW));
-                        cx.scope = ((TryMarker)o).scope;
-                        cx.pc = ((TryMarker)o).finallyLoc - 1;
-                        continue OUTER;
-                    }
-                }
-                // no handler found within this func
-                if(o instanceof CallMarker) throw e;
-            }
-            throw e;
-        } // end try/catch
-        } // end for
-    }
-
-
     // Debugging //////////////////////////////////////////////////////////////////////
 
     public String toString() { return "JSFunction [" + sourceName + ":" + firstLine + "]"; }
@@ -559,54 +113,5 @@ public class JSFunction extends JSCallable implements ByteCodes, Tokens {
     } 
 
 
-    // Exception Stuff ////////////////////////////////////////////////////////////////
-
-    static class EvaluatorException extends RuntimeException { public EvaluatorException(String s) { super(s); } }
-    static EvaluatorException ee(String s) {
-        throw new EvaluatorException(JSContext.getSourceName() + ":" + JSContext.getLine() + " " + s);
-    }
-    static JS.Exn je(String s) {
-        throw new JS.Exn(JSContext.getSourceName() + ":" + JSContext.getLine() + " " + s);
-    }
-
-
-    // Markers //////////////////////////////////////////////////////////////////////
-
-    public static class CallMarker {
-        int pc;
-        JSScope scope;
-        JSFunction f;
-        public CallMarker(JSContext cx) { pc = cx.pc + 1; scope = cx.scope; f = cx.f; }
-    }
-    
-    public static class CatchMarker { public CatchMarker() { } }
-    private static CatchMarker catchMarker = new CatchMarker();
-    
-    public static class LoopMarker {
-        public int location;
-        public String label;
-        public JSScope scope;
-        public LoopMarker(int location, String label, JSScope scope) {
-            this.location = location;
-            this.label = label;
-            this.scope = scope;
-        }
-    }
-    public static class TryMarker {
-        public int catchLoc;
-        public int finallyLoc;
-        public JSScope scope;
-        public TryMarker(int catchLoc, int finallyLoc, JSScope scope) {
-            this.catchLoc = catchLoc;
-            this.finallyLoc = finallyLoc;
-            this.scope = scope;
-        }
-    }
-    public static class FinallyData {
-        public int op;
-        public Object arg;
-        public FinallyData(int op, Object arg) { this.op = op; this.arg = arg; }
-        public FinallyData(int op) { this(op,null); }
-    }
 }
 
index 3eefe14..56a076a 100644 (file)
@@ -38,12 +38,9 @@ public class JSTrap {
     public static interface JSTrappable {
         public abstract JSTrap getTrap(Object key);
         public abstract void putTrap(Object key, JSTrap trap);
-
-        /** puts to this value using an unpauseable context, triggering any traps */
         public abstract void putAndTriggerJSTraps(Object key, Object value);
     }
 
-    // FIXME: cascadeHappened gets set, but autocascade does not happen
     static class JSTrapScope extends JSScope {
         JSTrap t;
         Object val = null;
index 21fe7e5..ff6369e 100644 (file)
@@ -4,29 +4,58 @@ package org.xwt.mips;
 import java.util.*;
 import java.io.*;
 
-// FIXME: lb/sb/sh/lh need not be word aligned
-// FIXME: memory accesses aren't handling sign-extending properly
-// FIXME: probably have to implement nonaligned access
-// FIXME: implement malloc()
-// FIXME: implement an ELF parser based on RandomAccessFile
 // FEATURE: progress indicator
-// FEATURE: support n32 abi (passes more arguments in registers)
-// FEATURE: trap on arithmetic overflows
-// FEATURE: FPU
-// FEATURE: we always know the value of the pc register; we should emit it as a literal when it appears in computations
-// FEATURE: emit bytecode rather than .java code (for on-the-fly classloading without javac present in "real" JVMs)
+// FEATURE: emit bytecode rather than .java code (for on-the-fly classloading without javac present in "real" JVMs
 
-/** reads a fully linked MIPS ELF binary image on stdin; writes a .java file on stdout */
-public class Compiler {
+public class Compiler implements Registers {
 
-    static String runs = "";
-    static int last_emit = -1;
-    static DataInputStream dis;
-    public static void main(String[] s) throws IOException {
+    private static StringBuffer runs = new StringBuffer();
+    private static StringBuffer inits = new StringBuffer();
+    
+    private static PrintStream out = System.out;
+    
+    private static int indent;
+    private static String indents[] = new String[16];
+    static { String s=""; for(int i=0;i<indents.length;i++,s=s+"    ") indents[i] = s; }
+    private static final void p() { out.println(); }
+    private static final void p(String s) { out.println(indents[indent] + s); }
+    
+    // FEATURE: This should probably provide some detail about where the excpetion happened (address, line numbers, etc)
+    private static class CompilationException extends Exception { public CompilationException(String s) { super(s); } }
+    
+    // Set this to true to enable fast memory access 
+    // When this is enabled a Java RuntimeException will be thrown when a page fault occures. When it is disabled
+    // a FaultException will be throw which is easier to catch and deal with, however. as the name implies, this is slower
+    private static boolean fastMem = true;
+    
+    // This MUST be a power of two. If it is not horrible things will happen
+    // NOTE: This value can be much higher without breaking the classfile 
+    // specs (around 1024) but Hotstop seems to do much better with smaller
+    // methods. 
+    private static int MAX_INSN_PER_METHOD = 32;
+    
+    // Don't touch this
+    private static int MAX_BYTES_PER_METHOD = MAX_INSN_PER_METHOD*4;
+    private static int METHOD_MASK = ~(MAX_BYTES_PER_METHOD-1);
+    
+    // Store frequently used registers in local variables
+    // Unfortunately this doesn't seem to speed things up much
+    private static String[] freqRegs = { /*"r2", "r29", "r3", "r16", "r5", "r17", "r6", "r18", "r4", "r31", "r19"*/ };
+
+    // True to try to determine which case statement are needed and only include them
+    private static boolean pruneCases = true;
+    
+    // True to insert some code in the output to help diagnore compiler problems
+    private final static boolean debugCompiler = false;
+    
+    // True to print various statistics about the compilation
+    private final static boolean printStats = true;
+            
+    public static void main(String[] s) throws Exception {
 
         if (s.length != 2) {
             System.err.println("usage: java " + Compiler.class.getName() + " <classname> <binary.mips>");
-            System.exit(-1);
+            System.exit(1);
         }
 
         String packageName = null;
@@ -35,160 +64,360 @@ public class Compiler {
             packageName = s[0].substring(0, s[0].lastIndexOf('.'));
             className = s[0].substring(s[0].lastIndexOf('.') + 1);
         }
-
-        System.out.println(prefix + "// This file was generated by MipsToJava");
-        if (packageName != null) System.out.println(prefix + "package " + packageName + ";");
-        System.out.println(prefix + "public class " + className + " extends org.xwt.mips.VM {");
-        System.out.println(prefix + "");
-        System.out.println(prefix + "    public " + className + "() { }");
-        System.out.println(prefix + "");
-        System.out.println(prefix + "    // program counter");
-        System.out.println(prefix + "    int pc = 0;");
-        System.out.println(prefix + "");
-        System.out.println(prefix + "    // temporary");
-        System.out.println(prefix + "    int tmp = 0;");
-        System.out.println(prefix + "");
-        System.out.println(prefix + "    // MIPS multiply/divide subsystem; 64-bit result");
-        System.out.println(prefix + "    long hilo = 0;");
-        System.out.println(prefix + "");
-        System.out.println(prefix + "    // General Purpose registers");
-        System.out.println(prefix + "    final int r0 = 0;");
-        System.out.println(prefix + "    int r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0,");
-        System.out.println(prefix + "        r8 = 0, r9 = 0, r10 = 0, r11 = 0, r12 = 0, r13 = 0, r14 = 0, r15 = 0,");
-        System.out.println(prefix + "        r16 = 0, r17 = 0, r18 = 0, r19 = 0, r20 = 0, r21 = 0, r22 = 0, r23 = 0,");
-        System.out.println(prefix + "        r24 = 0, r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0, r30 = 0, r31 = 0;");
-        System.out.println(prefix + "");
-
-        ELF elf = new ELF(new RandomAccessFile(args[1], "r"));
+        
+        ELF elf = new ELF(s[1]);
         if(elf.header.type != ELF.ELFHeader.ET_EXEC) throw new IOException("Binary is not an executable");
         if(elf.header.machine != ELF.ELFHeader.EM_MIPS) throw new IOException("Binary is not for the MIPS I Architecture");
 
-        ELF.PHeader[] pheaders = elf.pheaders;
-
-        int[][] readPages = new int[TOTAL_PAGES][];
-        int[][] writePages = new int[TOTAL_PAGES][];
-        for(int i=0; i<STACK_PAGES; i++)
-            readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage;
-
-        // FIXME: what is this?
-        int brk = 0;
-        for(int i=0;i<pheaders.length;i++) {
-            ELF.PHeader ph = pheaders[i];
-            if(ph.type != ELF.PHeader.PT_LOAD) continue;
-            int memsize = ph.memsz;
-            int filesize = ph.filesz;
-            if(memsize == 0) continue;
-            if(memsize < 0) throw new IOException("pheader size too large");
-            int addr = ph.vaddr;
-            if(addr == 0x0) throw new IOException("pheader vaddr == 0x0");
-            if(addr+memsize >= (brk<<PAGE_SHIFT)) brk = (addr+memsize+PAGE_SIZE-1) >> PAGE_SHIFT;
-            // FIXME: set memsize, serialize readPages/writePages
-            for(int j=0;j<memsize+PAGE_SIZE-1;j+=PAGE_SIZE) {
-                int page = (j+addr) >>> PAGE_SHIFT;
-                if(readPages[page] == null) readPages[page] = new int[PAGE_WORDS];
-                if(ph.writable()) writePages[page] = readPages[page];
+        p("// This file was generated by MipsToJava");
+        if (packageName != null) p("package " + packageName + ";");
+        p("public class " + className + " extends Runtime {");
+        p("");
+        p("    // program counter");
+        p("    private int pc = 0;");
+        if(debugCompiler)
+            p("    private int lastPC = 0;");
+        p("");
+        p("    // General Purpose registers");
+        p("    private final static int r0 = 0;");
+        p("    int r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0,");
+        p("        r8 = 0, r9 = 0, r10 = 0, r11 = 0, r12 = 0, r13 = 0, r14 = 0, r15 = 0,");
+        p("        r16 = 0, r17 = 0, r18 = 0, r19 = 0, r20 = 0, r21 = 0, r22 = 0, r23 = 0,");
+        p("        r24 = 0, r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0, r30 = 0, r31 = 0,");
+        p("        hi = 0, lo = 0;");
+        p("    // FP registers");
+        p("    private int f0 = 0, f1 = 0, f2 = 0, f3 = 0, f4 = 0, f5 = 0, f6 = 0, f7 = 0,");
+        p("        f8 = 0, f9 = 0, f10 = 0, f11 = 0, f12 = 0, f13 = 0, f14 = 0, f15 = 0,");
+        p("        f16 = 0, f17 = 0, f18 = 0, f19 = 0, f20 = 0, f21 = 0, f22 = 0, f23 = 0,");
+        p("        f24 = 0, f25 = 0, f26 = 0, f27 = 0, f28 = 0, f29 = 0, f30 = 0, f31 = 0;");
+        p("    // FP Control Register");        
+        p("    private int fcsr = 0;");
+        p("");
+        indent++;
+        // These should all be inlind by javac
+        p("private final void setFC(boolean b) { fcsr = (fcsr&~0x800000) | (b ? 0x800000 : 0x000000); }");
+        p("private final int roundingMode() { return fcsr & 3; /* bits 0-1 */ }");
+        indent--;
+        
+        Set jumpableAddresses = null;
+        if(pruneCases) {
+            // Find all possible branches
+            jumpableAddresses = new HashSet();
+            
+            jumpableAddresses.add(new Integer(elf.header.entry));
+            
+            ELF.SHeader text = elf.sectionWithName(".text");
+            if(text == null) throw new Error("No .text segment");
+            findBranchesInText(text.addr,new DataInputStream(text.getInputStream()),text.size,jumpableAddresses);
+            
+            findBranchesInSymtab(elf.getSymtab(),jumpableAddresses);            
+            
+            for(int i=0;i<elf.sheaders.length;i++) {
+                ELF.SHeader sheader = elf.sheaders[i];
+                String name = sheader.name;
+                // if this section doesn't get loaded into our address space don't worry about it
+                if(sheader.addr == 0x0) continue;
+                if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
+                    findBranchesInData(new DataInputStream(sheader.getInputStream()),sheader.size,jumpableAddresses,text.addr,text.addr+text.size);
             }
-            if(filesize != 0) {
-                filesize = filesize & ~3;
-                DataInputStream dis = new DataInputStream(ph.getInputStream());
-                do {
-                    if (ph.executable()) emit(addr, 1, dis);
-                    readPages[addr >>> PAGE_SHIFT][(addr >>> 2)&(PAGE_WORDS-1)] = dis.readInt();
-                    addr+=4;
-                    filesize-=4;
-                } while(filesize > 0);
-                dis.close();
+        }
+        
+        // Generate main body functions
+        int highestAddr = 0;
+        indent=1;
+        for(int i=0;i<elf.sheaders.length;i++) {
+            ELF.SHeader sheader = elf.sheaders[i];
+            String name = sheader.name;
+            // if this section doesn't get loaded into our address space don't worry about it
+            if(sheader.addr == 0x0) continue;
+            
+            highestAddr = Math.max(highestAddr, sheader.addr + sheader.size);
+            
+            if(name.equals(".text")) {
+                emitText(sheader.addr, new DataInputStream(sheader.getInputStream()),sheader.size,jumpableAddresses);
+                endMethod(nextEmitTextAddr);
+            } else if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors")) {
+                emitData(sheader.addr, new DataInputStream(sheader.getInputStream()), sheader.size,name.equals(".rodata")); 
+            } else if(name.equals(".bss") || name.equals(".sbss")) {
+                if(sheader.entsize != 0) throw new CompilationException("bss segment has data!");
+                emitBSS(sheader.addr,sheader.size);
+            } else {
+                throw new CompilationException("Unknown segment: " + name);
             }
+        }        
+        indent = 0;
+        
+        p("    public " + className + "() throws FaultException {");
+        if(fastMem) {
+            p("        super(false); // don't allow empty pages");
+            p("        if(PAGE_SIZE != " + toHex(Runtime.PAGE_SIZE) + ") throw new Error(\"Runtime.PAGE_SIZE mismatch\");");
+        } else {
+            p("        super(true); // allow empty pages");
         }
+        p("        // init data");
+        p("        entryPoint = " + toHex(elf.header.entry) + ";");
+        p(inits.toString());
+        p("        brk = (" + toHex(highestAddr) + "+PAGE_SIZE-1) >>> PAGE_SHIFT;");
+        p("        state = INITIALIZED;");
+        p("    }");
+        p("");
+        p();
+        p("    public static void main(String[] javaArgs) throws Exception {");
+        p("        String[] args = new String[javaArgs.length+1];");
+        p("        System.arraycopy(javaArgs,0,args,1,javaArgs.length);");
+        p("        args[0] = \"" + className + "\";");
+        p("        " + className + " me = new " + className + "();");
+        p("        // User data");
+        p("        int addr = me.sbrk(PAGE_SIZE);");
+        p("        for(int i=0;i<10;i++) {");
+        p("            String s = \"User Info item: \" + (i+1) + \"\\0\";");
+        p("            byte[] b = s.getBytes(\"US-ASCII\");");
+        p("            me.copyout(b,addr,b.length);");
+        p("            me.setUserInfo(i,addr);");
+        p("            addr += b.length;");
+        p("        }");
+        p("        // End user data");
+        p("        int status = me.run(args);");
+        p("        System.err.println(\"Exit status: \" + status);");
+        p("        System.exit(status);");
+        p("        }");
+        p();
+        p("    protected void _start(int pc) {");
+        p("        // set the stack pointer");
+                p("        r26 = STUFF_BASE;");
+                p("        r27 = PAGE_SIZE;");
+        p("        r29 = INITIAL_SP;");
+        p("        // set the \"return address\" from _start to point at the \"magic exit address\" (0xdeadbeef)");
+        p("        r31 = 0xdeadbeef;");
+        p("        this.pc = pc;");
+        p("    }");
 
-        // cap off the last method
-        emit(-1, -1, dis);
-
-        System.out.println(prefix + "    protected int state = INITIALIZED;");
-        System.out.println(prefix + "    protected int brk = " + brk + ";");
+        p();
+        p("    protected void _execute() throws ExecutionException { trampoline(); }");
+        p();
+        p("    private final void trampoline() throws ExecutionException {");
+        p("        boolean finished = false;");
+        p("        while(!finished) {");
+        p("            switch(this.pc & " + toHex(~(MAX_BYTES_PER_METHOD-1)) + ") {");
+        p(runs.toString());
+        p("                default: throw new Error(\"invalid address 0x\" + Long.toString(this.pc&0xffffffffL,16));");
+        p("            }");
+        p("        }");
+        p("    }");
+        p("}");
+    }
 
-        System.out.println();
-        System.out.println(prefix + "    public static void main(String[] s) { new " + className + "()._start(entryPoint); }");
-        System.out.println();
-        System.out.println(prefix + "    protected int entryPoint = " + elf.header.entry + ";");
-        System.out.println();
-        System.out.println(prefix + "    protected void _start(int pc) {");
-        System.out.println();
-        System.out.println(prefix + "        // set the stack pointer");
-        System.out.println(prefix + "        r29 = INITIAL_SP;");
-        System.out.println();
-        System.out.println(prefix + "        // set the \"return address\" from _start to point at the \"magic exit address\" (0xdeadbeef)");
-        System.out.println(prefix + "        r31 = 0xdeadbeef;");
-        System.out.println();
-        System.out.println(prefix + "        // read in the .data segment");
-        System.out.println(prefix + "        //initData();");
-        System.out.println();
-        System.out.println(prefix + "        trampoline(pc);");
-        System.out.println();
-        System.out.println(prefix + "    }");
+    private static int startOfMethod = 0;
+    private static int endOfMethod = 0;
+    
+    private static void startMethod(int addr) {
+        addr &= ~(MAX_BYTES_PER_METHOD-1);
+        endOfMethod = addr + MAX_BYTES_PER_METHOD;
+        String methodName = "run_" + Long.toString(addr & 0xffffffffL, 16);
+        runs.append(indents[4] + "case " + toHex(addr) + ": finished = !" + methodName + "(); break;\n");
+        p("private final boolean " + methodName + "() throws ExecutionException { /"+"* " + toHex(addr) + " - " + toHex(endOfMethod) + " *" + "/");
+        indent++;
+        p("int addr, tmp;");
+        for(int i=0;i<freqRegs.length;i++)
+            p("int " + freqRegs[i] + " = this." + freqRegs[i] + ";");
+        p("for(;;) {");
+        indent++;
+        p("switch(pc) {");
+        indent++;
+        startOfMethod = addr;
+        
+    }
+    private static void endMethod() { endMethod(endOfMethod); }
+    private static void endMethod(int lastAddr) {
+        if(startOfMethod == 0) return;
+        // This isn't strictly necessary; its just here to work around unreachable code errors
+        p("case " + toHex(lastAddr) + ":");
+        indent++;
+        p("pc=" + toHex(lastAddr) + ";");
+        leaveMethod();
+        indent--;
+        if(debugCompiler)
+            p("default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16)  + \" (got here from 0x\" + Long.toString(lastPC&0xffffffffL,16)+\")\");");
+        else
+            p("default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
+        indent--;
+        p("}"); // end switch
+        p("/* NOT REACHED */");
+        indent--;
+        p("}"); // end for
+        indent--;
+        p("}"); // end method
+        endOfMethod = 0;
+    }
 
-        System.out.println();
-        System.out.println(prefix + "    public void execute() throws EmulationException {");
-        System.out.println(prefix + "        if(state == PAUSED) state = RUNNING;");
-        System.out.println(prefix + "        if(state != RUNNING)");
-        System.out.println(prefix + "             throw new IllegalStateException(\"execute() called in inappropriate state\");");
-        System.out.println(prefix + "        trampoline(this.pc);");
-        System.out.println(prefix + "    }");
-        System.out.println();
-        System.out.println(prefix + "    public void trampoline(int pc) {");
-        System.out.println(prefix + "        this.pc = pc;");
-        System.out.println(prefix + "        while(true) {");
-        System.out.println(prefix + "            switch(this.pc & 0xffffff00) {");
-        System.out.println(prefix + "                case 0xdeadbe00: System.out.println(\"exiting with return value \" + r2); System.exit(r2); continue;");
-        System.out.print(runs);
-        System.out.println(prefix + "                default: throw new Error(\"invalid address 0x\" + Long.toString(this.pc&0xffffffffL,16));");
-        System.out.println(prefix + "            }");
-        System.out.println(prefix + "        }");
-        System.out.println(prefix + "    }");
-        System.out.println(prefix + "}");
+    private static void branch(int pc, int target) {
+        if(debugCompiler)
+            p("lastPC = " + toHex(pc) + ";");
+        p("pc=" + toHex(target) + ";");
+        if((pc&METHOD_MASK) == (target&METHOD_MASK))
+            p("continue;");
+        else
+            leaveMethod();
+    }
+    
+    private static void leaveMethod() { leaveMethod(true); }
+    private static void leaveMethod(boolean cont) {
+        for(int i=0;i<freqRegs.length;i++)
+            p("this." + freqRegs[i] + " = " + freqRegs[i] + ";");
+        p("return " + (cont?"true":"false") + ";");
     }
 
-    static int _instruction;
-    static boolean readnext = true;
+    private static int nextEmitTextAddr = -1;
+    private static void emitText(int addr, DataInputStream dis, int size, Set jumpableAddresses) throws CompilationException,IOException {
+        if(addr < nextEmitTextAddr) throw new CompilationException("Out of order sections");
+        if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
+        int count = size/4;
+        int nextInsn = dis.readInt();
+        if(nextInsn == -1) throw new Error("Actually read -1 at " + toHex(addr));
+        int insn;
 
-    /** reads <tt>numbytes</tt> from the stream, emitting <tt>case</tt> blocks starting at vaddr <tt>ofs</tt> */
-    static void emit(int vaddr, int numbytes, DataInputStream dis) throws IOException {
-        if ((vaddr == -1 && numbytes == -1) || (last_emit != -1 && ((last_emit & 0xffffff00) != (vaddr & 0xffffff00)))) {
-            System.out.println(prefix + "                case 0x" + Long.toString((last_emit & 0xffffffffL) + 0x1000, 16) + ": return;");
-            System.out.println(prefix + "                default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
-            System.out.println(prefix + "        }");
-            System.out.println(prefix + "    }");
-            if (vaddr == -1 && numbytes == -1) return;
+        for(int i=0;i<count;i++,addr+=4) {
+            insn = nextInsn;
+            nextInsn = (i == count-1) ? -1 : dis.readInt();
+            if(addr >= endOfMethod) { endMethod(); startMethod(addr); }
+            if(jumpableAddresses==null || addr == startOfMethod || jumpableAddresses.contains(new Integer(addr))) {
+                p("case " + toHex(addr) + ":");
+                unreachable = false;
+            } else if(unreachable) {
+                continue;
+            } else if(debugCompiler) {
+                p("/" + "* pc = " + toHex(addr) + "*" + "/");
+            }
+            indent++;
+            emitInstruction(addr,insn,nextInsn);
+            indent--;
         }
-        if (last_emit == -1 || ((last_emit & 0xffffff00) != (vaddr & 0xffffff00))) {
-            System.out.println("");
-            System.out.println(prefix + "    private void run_" + Long.toString(vaddr & 0xffffff00L,16) + "() {");
-            runs += "                case 0x" + Long.toString(vaddr & 0xffffff00L,16) +
-                ": run_" + Long.toString(vaddr & 0xffffff00L,16) + "(); continue;\n";
-            System.out.println(prefix + "        switch(pc) {");
+        nextEmitTextAddr = addr;
+        dis.close();
+    }
+    
+    private static int initDataCount = 0;
+    private static void emitData(int addr, DataInputStream dis, int size, boolean readOnly) throws CompilationException,IOException {
+        if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
+        int count = size/4;
+        String varname =  "_data" + (++initDataCount);
+        p("private final static int[] " + varname +  " = {");
+        indent++;
+        for(int i=0;i<count;) {
+            StringBuffer sb = new StringBuffer();
+            for(int j=0;j<8 && i<count;j++,i++) {
+                sb.append(toHex8(dis.readInt()));
+                if(i!=count-1) sb.append(",");
+            }
+            p(sb.toString());
         }
-        int ofs = vaddr;
-        try {
-            OUTER: for(ofs = vaddr; ofs < vaddr + numbytes; ofs+=4) {
-                if (readnext) _instruction = dis.readInt();
-                readnext = true;
-
-                String istring = Long.toString(_instruction & 0xffffffffL, 16);
-                while(istring.length() < 8) istring = "0" + istring;
-                String ostring = Long.toString(ofs & 0xffffffffL, 16);
-                while(ostring.length() < 8) ostring = "0" + ostring;
-                System.out.print(prefix + "                /* " + istring + " */   case 0x" + ostring + ": System.out.println(\"pc=0x\" + Long.toString(pc&0xffffffffL,16));");
-
-                emit_instruction(ofs, _instruction);
+        indent--;
+        p("};");
+        inits.append(indents[2] + "initPages(" + varname + "," + toHex(addr) + "," + (readOnly?"true":"false") + ");\n");
+        dis.close();
+    }
+    
+    private static int initBSSCount = 0;
+    private static void emitBSS(int addr, int size) throws CompilationException,IOException {
+        if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
+        int count = size/4;
+        inits.append(indents[2] + "clearPages(" + toHex(addr) + "," + toHex(count) + ");\n");
+    }
+    
+    private static void findBranchesInSymtab(ELF.Symtab symtab, Set jumps) {
+        ELF.Symbol[] symbols = symtab.symbols;
+        int n=0;
+        for(int i=0;i<symbols.length;i++) {
+            ELF.Symbol s = symbols[i];
+            if(s.type == ELF.Symbol.STT_FUNC) {
+                //System.err.println("Adding symbol: " + s.name + " at " + toHex(s.addr));
+                if(jumps.add(new Integer(s.addr))) n++;
             }
-        } catch (EOFException e) {
-            emit(ofs, "                // warning, reached EOF before section end");
-        } finally {
-            last_emit = ofs;
-        }  
+        }
+        if(printStats) System.err.println("Found " + n + " additional possible branch targets in Symtab");
     }
-
-    private static void emit_instruction(int pc, int insn) throws IOException {
+    
+    private static void findBranchesInText(int addr, DataInputStream dis, int size, Set jumps) throws IOException {
+        int count = size/4;
+        int pc = addr;
+        int n=0;
+        
+        for(int i=0;i<count;i++,pc+=4) {
+            int insn = dis.readInt();
+            int op = (insn >>> 26) & 0xff; 
+            int rs = (insn >>> 21) & 0x1f;
+            int rt = (insn >>> 16) & 0x1f;
+            int signedImmediate = (insn << 16) >> 16;
+            int branchTarget = signedImmediate;
+            int jumpTarget = (insn & 0x03ffffff);
+            int subcode = insn & 0x3f;
+            
+            switch(op) {
+                case 0:
+                    switch(subcode) {
+                        case 9: // JALR
+                            if(jumps.add(new Integer(pc+8))) n++; // return address
+                            break;
+                        case 12: // SYSCALL
+                            if(jumps.add(new Integer(pc+4))) n++; 
+                            break;
+                    }
+                    break;
+                case 1:
+                    switch(rt) {
+                        case 16: // BLTZAL
+                        case 17: // BGTZAL
+                            if(jumps.add(new Integer(pc+8))) n++; // return address
+                            // fall through
+                        case 0: // BLTZ
+                        case 1: // BGEZ
+                            if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
+                            break;
+                    }
+                    break;
+                case 3: // JAL
+                    if(jumps.add(new Integer(pc+8))) n++; // return address
+                    // fall through
+                case 2: // J
+                    if(jumps.add(new Integer((pc&0xf0000000)|(jumpTarget << 2)))) n++;
+                    break;
+                case 4: // BEQ
+                case 5: // BNE
+                case 6: // BLEZ
+                case 7: // BGTZ
+                    if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
+                    break;
+                case 17: // FPU Instructions
+                    switch(rs) {
+                        case 8: // BC1F, BC1T
+                            if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
+                            break;
+                    }
+                    break;
+            }
+        }
+        dis.close();
+        if(printStats) System.err.println("Found " + n + " additional possible branch targets in Text segment");
+    }
+    
+    private static void findBranchesInData(DataInputStream dis, int size, Set jumps, int textStart, int textEnd) throws IOException {
+        int count = size/4;
+        int n=0;
+        for(int i=0;i<count;i++) {
+            int word = dis.readInt();
+            if((word&3)==0 && word >= textStart && word < textEnd) {
+                if(jumps.add(new Integer(word))) n++;
+            }
+        }
+        dis.close();
+        if(n>0 && printStats) System.err.println("Found " + n + " additional possible branch targets in Data segment");
+    }
+    
+    private static boolean unreachable = false;
+    
+    private static void emitInstruction(int pc, int insn, int nextInsn) throws IOException,CompilationException {
+        if(insn == -1) throw new Error("insn is -1");
+        
         int op = (insn >>> 26) & 0xff;                 // bits 26-31
         int rs = (insn >>> 21) & 0x1f;                 // bits 21-25
         int rt = (insn >>> 16) & 0x1f;                 // bits 16-20 
@@ -206,99 +435,125 @@ public class Compiler {
 
         int tmp, addr; // temporaries
         
+        //if(pc%64==0) p("System.err.println(\"Executing: " + toHex(pc) + "\");");
+        //p("/" + "*" + (pc == -1 ? "Delay Slot"  : toHex(pc)) + " *" + "/ ");
+        if(pc==-1) p("/" + "* Next insn is delay slot *" + "/ ");
+        
         switch(op) {
             case 0: {
                 switch(subcode) {
                     case 0: // SLL
-                        if(insn == 0) break;
-                        emit(pc, "r"+rd+" = r"+rt+" << "+shamt+";");
+                        if(insn == 0) 
+                            p("/* NOOP */"); 
+                        else
+                            p( "r"+rd+" = r"+rt+" << "+shamt+";");
                         break;
                     case 2: // SRL
-                        emit(pc, "r"+rd+" = r"+rt+" >>> "+shamt+";");
+                        p( "r"+rd+" = r"+rt+" >>> "+shamt+";");
                         break;
                     case 3: // SRA
-                        emit(pc, "r"+rd+" = r"+rt+" >> "+shamt+";");
+                        p( "r"+rd+" = r"+rt+" >> "+shamt+";");
                         break;
-                        // FIXME: Do we need % 32 on the r"+rs+" ?
                     case 4: // SLLV
-                        emit(pc, "r"+rd+" = r"+rt+" << r"+rs+";");
+                        p( "r"+rd+" = r"+rt+" << (r"+rs+"&0x1f);");
                         break;
                     case 6: // SRLV
-                        emit(pc, "r"+rd+" = r"+rt+" >>> r"+rs+";");
+                        p( "r"+rd+" = r"+rt+" >>> (r"+rs+"&0x1f);");
                         break;
                     case 7: // SRAV
-                        emit(pc, "r"+rd+" = r"+rt+" >> r"+rs+";");
+                        p( "r"+rd+" = r"+rt+" >> (r"+rs+"&0x1f);");
                         break;
                     case 8: // JR
-                        emit(pc, "{ int tmp = r"+rs+"; pc += 4; nextPC = tmp; }");
-                        continue OUTER;
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        emitInstruction(-1,nextInsn,-1);
+                        if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
+                        p("pc=r" + rs + ";");
+                        leaveMethod();
+                        unreachable = true;
+                        break;
                     case 9: // JALR
-                        emit(pc, "{ int tmp = r"+rs+"; pc += 4; r"+rd+" = pc+4; nextPC = tmp; }");
-                        continue OUTER;
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        emitInstruction(-1,nextInsn,-1);
+                        if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
+                        p("pc=r" + rs + ";");
+                        p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
+                        leaveMethod();
+                        unreachable = true;
+                        break;
                     case 12: // SYSCALL
-                        emit(pc, "r"+V0+" = syscall(r"+V0+",r"+A0+",r"+A1+",r"+A2+",r"+A3+"); " +
-                             "if (state != RUNNING) { this.nextPC = nextPC; return; }");
+                        p( "r"+V0+" = syscall(r"+V0+",r"+A0+",r"+A1+",r"+A2+",r"+A3+");");
+                        p("if (state != RUNNING) {");
+                            indent++;
+                            p("pc = " + toHex(pc+4) + ";");
+                            leaveMethod(false);
+                            indent--;
+                        p("}");
                         break;
                     case 13: // BREAK
-                        emit(pc, "throw new EmulationException(\"Break\")");
+                        p( "throw new ExecutionException(\"Break\");");
                         break;
                     case 16: // MFHI
-                        emit(pc, "r"+rd+" = hi;");
+                        p( "r"+rd+" = hi;");
                         break;
                     case 17: // MTHI
-                        emit(pc, "hi = r"+rs+";");
+                        p( "hi = r"+rs+";");
                         break;
                     case 18: // MFLO
-                        emit(pc, "r"+rd+" = lo;");
+                        p( "r"+rd+" = lo;");
                         break;
                     case 19: // MTLO
-                        emit(pc, "lo = r"+rs+";");
+                        p( "lo = r"+rs+";");
                         break;
                     case 24: // MULT
-                        emit(pc, "long hilo = (long)(r"+rs+") * ((long)r"+rt+"); " +
+                        p( "{ long hilo = (long)(r"+rs+") * ((long)r"+rt+"); " +
                              "hi = (int) (hilo >>> 32); " +
-                             "lo = (int) hilo;");
+                             "lo = (int) hilo; }");
                         break;
                     case 25: // MULTU
-                        emit(pc, "long hilo = (r"+rs+" & 0xffffffffL) * (r"+rt+" & 0xffffffffL); " +
+                        p( "{ long hilo = (r"+rs+" & 0xffffffffL) * (r"+rt+" & 0xffffffffL); " +
                              "hi = (int) (hilo >>> 32); " +
-                             "lo = (int) hilo;");
+                             "lo = (int) hilo; } ");
+                        break;
                     case 26: // DIV
-                        emit(pc, "hi = r"+rs+"%r"+rt+"; lo = r"+rs+"/r"+rt+";");
+                        p( "hi = r"+rs+"%r"+rt+"; lo = r"+rs+"/r"+rt+";");
                         break;
                     case 27: // DIVU
-                        emit(pc, "hi = (int)((r"+rs+" & 0xffffffffL) % (r"+rt+" & 0xffffffffL)); " +
+                        p( "hi = (int)((r"+rs+" & 0xffffffffL) % (r"+rt+" & 0xffffffffL)); " +
                              "lo = (int)((r"+rs+" & 0xffffffffL) / (r"+rt+" & 0xffffffffL));");
                         break;
                     case 32: // ADD
-                        emit(pc, "r"+rd+" = r"+rs+" + r"+rt+";"); // FIXME: Trap on overflow
-                        break;
+                        throw new CompilationException("ADD (add with oveflow trap) not suported");
+                        /*This must trap on overflow
+                        p( "r"+rd+" = r"+rs+" + r"+rt+";");
+                        break;*/
                     case 33: // ADDU
-                        emit(pc, "r"+rd+" = r"+rs+" + r"+rt+";");
+                        p( "r"+rd+" = r"+rs+" + r"+rt+";");
                         break;
                     case 34: // SUB
-                        emit(pc, "r"+rd+" = r"+rs+" - r"+rt+";");  // FIXME: Trap on overflow
-                        break;
+                        throw new CompilationException("SUB (add with oveflow trap) not suported");
+                        /*This must trap on overflow
+                        p( "r"+rd+" = r"+rs+" - r"+rt+";");
+                        break;*/
                     case 35: // SUBU
-                        emit(pc, "r"+rd+" = r"+rs+" - r"+rt+";");
+                        p( "r"+rd+" = r"+rs+" - r"+rt+";");
                         break;
                     case 36: // AND
-                        emit(pc, "r"+rd+" = r"+rs+" & r"+rt+";");
+                        p( "r"+rd+" = r"+rs+" & r"+rt+";");
                         break;
                     case 37: // OR
-                        emit(pc, "r"+rd+" = r"+rs+" | r"+rt+";");
+                        p( "r"+rd+" = r"+rs+" | r"+rt+";");
                         break;
                     case 38: // XOR
-                        emit(pc, "r"+rd+" = r"+rs+" ^ r"+rt+";");
+                        p( "r"+rd+" = r"+rs+" ^ r"+rt+";");
                         break;
                     case 39: // NOR
-                        emit(pc, "r"+rd+" = ~(r"+rs+" | r"+rt+");");
+                        p( "r"+rd+" = ~(r"+rs+" | r"+rt+");");
                         break;
                     case 42: // SLT
-                        emit(pc, "r"+rd+" = r"+rs+" < r"+rt+" ? 1 : 0;");
+                        p( "r"+rd+" = r"+rs+" < r"+rt+" ? 1 : 0;");
                         break;
                     case 43: // SLTU
-                        emit(pc, "r"+rd+" = ((r"+rs+" & 0xffffffffL) < (r"+rt+" & 0xffffffffL)) ? 1 : 0;");
+                        p( "r"+rd+" = ((r"+rs+" & 0xffffffffL) < (r"+rt+" & 0xffffffffL)) ? 1 : 0;");
                         break;
                     default:
                         throw new RuntimeException("Illegal instruction 0/" + subcode);
@@ -308,411 +563,460 @@ public class Compiler {
             case 1: {
                 switch(rt) {
                     case 0: // BLTZ
-                        emit(pc, "if (r"+rs+"<0) { pc += 4; int tmp = pc + "+
-                             branchTarget+"*4; nextPC = tmp; }");
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        p("if(r" + rs + " < 0) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
                         break;
                     case 1: // BGEZ
-                        emit(pc, "if (r"+rs+">=0) { pc += 4; int tmp = pc + "+
-                             branchTarget+"*4; nextPC = tmp; }");
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        p("if(r" + rs + " >= 0) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
                         break;
                     case 16: // BLTZAL
-                        emit(pc, "if(r"+rs+" < 0) { pc += 4; r"+RA+" = pc+4; int tmp = pc + "+
-                             branchTarget+"*4; nextPC = tmp; }");
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        p("if(r" + rs + " < 0) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
                         break;
                     case 17: // BGEZAL
-                        emit(pc, "if(r"+rs+" >= 0) { pc += 4; r"+RA+" = pc+4; int tmp = pc + "+
-                             branchTarget+"*4; nextPC = tmp; }");
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        p("if(r" + rs + " >= 0) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
                         break;
                     default:
-                        throw new RuntimeException("Illegal Instruction");
+                        throw new RuntimeException("Illegal Instruction 1/" + rt);
                 }
                 break;
             }
             case 2: { // J
-                emit(pc, "int tmp = (pc&0xf0000000) | ("+jumpTarget+" << 2); pc+=4; nextPC = tmp;");
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                emitInstruction(-1,nextInsn,-1);
+                branch(pc,(pc&0xf0000000)|(jumpTarget << 2));
+                unreachable = true;
                 break;
             }
             case 3: { // JAL
-                emit(pc, "int tmp = (pc&0xf0000000) | ("+jumpTarget+" << 2); pc+=4; r"+RA+
-                     " = pc+4; nextPC = tmp;");
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                emitInstruction(-1,nextInsn,-1);
+                p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
+                branch(pc, (pc&0xf0000000)|(jumpTarget << 2));
+                unreachable = true;
                 break;
             }
             case 4: // BEQ
-                emit(pc, "if(r"+rs+" == r"+rt+") { pc += 4; int tmp = pc + "+
-                     branchTarget+"*4; nextPC = tmp; }");
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                p("// BEQ");
+                p("if(r" + rs + " == r" + rt + ") {");
+                    indent++;
+                    emitInstruction(-1,nextInsn,-1);
+                    branch(pc,pc+branchTarget*4+4);
+                    indent--;
+                p("}");
                 break;
-            case 5: // BNE                
-                emit(pc, "if(r"+rs+" != r"+rt+") { pc += 4; int tmp = pc + "+
-                     branchTarget+"*4; nextPC = tmp; }");
+            case 5: // BNE       
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                p("if(r" + rs + " != r" + rt + ") {");
+                    indent++;
+                    emitInstruction(-1,nextInsn,-1);
+                    branch(pc,pc+branchTarget*4+4);
+                    indent--;
+                p("}");
                 break;
             case 6: //BLEZ
-                emit(pc, "if(r"+rs+" <= 0) { pc += 4; int tmp = pc + "+
-                     branchTarget+"*4; nextPC = tmp; ;");
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                p("if(r" + rs + " <= 0) {");
+                    indent++;
+                    emitInstruction(-1,nextInsn,-1);
+                    branch(pc,pc+branchTarget*4+4);
+                    indent--;
+                p("}");
                 break;
             case 7: //BGTZ
-                emit(pc, "if(r"+rs+" > 0) { pc += 4; int tmp = pc + "+branchTarget+"*4; nextPC = tmp; }");
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                p("if(r" + rs + " > 0) {");
+                    indent++;
+                    emitInstruction(-1,nextInsn,-1);
+                    branch(pc,pc+branchTarget*4+4);
+                    indent--;
+                p("}");
                 break;
             case 8: // ADDI
-                emit(pc, "r"+rt+" = r"+rs+" + "+signedImmediate +";");
+                p( "r"+rt+" = r"+rs+" + "+signedImmediate +";");
                 break;
             case 9: // ADDIU
-                emit(pc, "r"+rt+" = r"+rs+" + "+signedImmediate+";");
+                p( "r"+rt+" = r"+rs+" + "+signedImmediate+";");
                 break;
             case 10: // SLTI
-                emit(pc, "r"+rt+" = r"+rs+" < "+signedImmediate+" ? 1 : 0;");
+                p( "r"+rt+" = r"+rs+" < "+signedImmediate+" ? 1 : 0;");
                 break;
             case 11: // SLTIU
-                emit(pc, "r"+rt+" = (r"+rs+"&0xffffffffL) < ("+unsignedImmediate+"&0xffffffffL) ? 1 : 0;");
+                p( "r"+rt+" = (r"+rs+"&0xffffffffL) < ("+unsignedImmediate+"&0xffffffffL) ? 1 : 0;");
                 break;
             case 12: // ANDI
-                emit(pc, "r"+rt+" = r"+rs+" & "+unsignedImmediate+";");
+                p( "r"+rt+" = r"+rs+" & "+unsignedImmediate+";");
                 break;
             case 13: // ORI
-                emit(pc, "r"+rt+" = r"+rs+" | "+unsignedImmediate+";");
+                p( "r"+rt+" = r"+rs+" | "+unsignedImmediate+";");
                 break;
             case 14: // XORI
-                emit(pc, "r"+rt+" = r"+rs+" ^ "+unsignedImmediate+";");
+                p( "r"+rt+" = r"+rs+" ^ "+unsignedImmediate+";");
                 break;
             case 15: // LUI
-                emit(pc, "r"+rt+" = "+unsignedImmediate+" << 16;");
+                p( "r"+rt+" = "+unsignedImmediate+" << 16;");
                 break;
             case 16:
-                throw new RuntimeException("TLB/Exception support not implemented");
+                throw new CompilationException("TLB/Exception support not implemented");
             case 17: { // FPU
                 switch(rs) {
                     case 0: // MFC.1
-                        emit(pc, "r"+rt+" = f"+rd);
+                        p( "r"+rt+" = f"+rd+";");
                         break;
                     case 2: // CFC.1
-                        if(fs != 31) throw new EmulationException("FCR " + fs + " unavailable");
-                        emit(pc, "r"+rt+" = fcsr;");
+                        if(fs != 31) throw new CompilationException("FCR " + fs + " unavailable");
+                        p( "r"+rt+" = fcsr;");
                         break;
                     case 4: // MTC.1
-                        emit(pc, "f"+rd+" = r"+rt+";");
+                        p( "f"+rd+" = r"+rt+";");
                         break;
                     case 6: // CTC.1
-                        if(fs != 31) throw new EmulationException("FCR " + fs + " unavailable");
-                        emit(pc, "fcsr = r"+rt+";  ;") 
-                        break;
-                    case 8: // BC1F, BC1T
-                        emit(pc, "if(((fcsr&0x800000)!=0) == (((insn>>>16)&1)!=0)) {"+
-                             " pc += 4; int tmp = pc + "+branchTarget+"*4; nextPC = tmp; ;")}
+                        if(fs != 31) throw new CompilationException("FCR " + fs + " unavailable");
+                        p( "fcsr = r"+rt+";");
+                        break;
+                    case 8: {// BC1F, BC1T
+                        tmp = (insn>>>16)&1;
+                        p("//BC1F, BC1T");
+                        p("if(((fcsr&0x800000)!=0) == (" + tmp + "!=0)) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
                         break;
-                    case 16: {  // Single
+                    }
+                    case 16: {  // Single 
                         switch(subcode) {
                             case 0: // ADD.S
-                                emit(pc, setFloat(fd,getFloat(fs)+"+"+getFloat(ft)));
+                                p(setFloat(fd,getFloat(fs)+"+"+getFloat(ft)));
                                 break;
                             case 1: // SUB.S
-                                emit(pc, setFloat(fd,getFloat(fs)+"-"+getFloat(ft)));
+                                p(setFloat(fd,getFloat(fs)+"-"+getFloat(ft)));
                                 break;
                             case 2: // MUL.S
-                                emit(pc, setFloat(fd,getFloat(fs)+"*"+getFloat(ft)));
+                                p(setFloat(fd,getFloat(fs)+"*"+getFloat(ft)));
                                 break;
                             case 3: // DIV.S
-                                emit(pc, setFloat(fd,getFloat(fs)+"/"+getFloat(ft)));
+                                p(setFloat(fd,getFloat(fs)+"/"+getFloat(ft)));
                                 break;
                             case 5: // ABS.S
-                                emit(pc, setFloat(fd,Math.abs("+getFloat(fs)+")));
+                                p(setFloat(fd,"Math.abs("+getFloat(fs)+")"));
                                 break;
                             case 6: // MOV.S
-                                emit(pc, f"+fd+" = f"+fs+");
+                                p("f"+fd+" = f"+fs+"; // MOV.S");
                                 break;
                             case 7: // NEG.S
-                                emit(pc, setFloat(fd,"-1 * ("+getFloat(fs)+")")); // FIXME: just flip the sign bi)t
+                                p(setFloat(fd,"-"+getFloat(fs))); // FEATURE: just flip the sign bit
                                 break;
                             case 33: // CVT.D.S
-                                emit(pc, setDouble(fd,getFloat(fs)));
+                                p(setDouble(fd,"(float)"+getFloat(fs)));
                                 break;
-                            case 36: // CVT.W.S
-                                switch(roundingMode()) {
-                                    case 0: emit(pc, "f"+fd+" = (int)Math.floor("+getFloat(fs)+"+0.5f;"); break; // Round to nearest
-                                    case 1: emit(pc, "f"+fd+" = (int)"+getFloat(fs)+";"); break; // Round towards zero
-                                    case 2: emit(pc, "f"+fd+" = (int)Math.ceil("+getFloat(fs)+";"); break; // Round towards plus infinity
-                                    case 3: emit(pc, "f"+fd+" = (int)Math.floor("+getFloat(fs)+";"); break; // Round towards minus infinity
-                                }
+                            case 36: // CVT.W.D
+                                p("// CVT.W.D");
+                                p("switch(roundingMode()) {");
+                                    indent++;
+                                    p("case 0: f"+fd+" = (int)Math.floor("+getFloat(fs)+"+0.5); break; // Round to nearest");
+                                    p("case 1: f"+fd+" = (int)"+getFloat(fs)+"; break; // Round towards zero");
+                                    p("case 2: f"+fd+" = (int)Math.ceil("+getFloat(fs)+"); break; // Round towards plus infinity");
+                                    p("case 3: f"+fd+" = (int)Math.floor("+getFloat(fs)+"); break; // Round towards minus infinity");
+                                    indent--;
+                                p("}");
                                 break;
-                                /* FIXME: not implemented yet
-                            case -50: // C.EQ.S
-                                setFC("+getFloat(fs)+" == "+getFloat(ft)+"); // FIXME: just compare the ints, be sure things are normalized
+                            case 50: // C.EQ.D
+                                p("setFC("+getFloat(fs)+"=="+getFloat(ft)+");"); // FEATURE: just compare the ints, be sure things are normalized
                                 break;
-                            case 60: // C.LT.S
-                                setFC("+getFloat(fs)+" < "+getFloat(ft)+");
+                            case 60: // C.LT.D
+                                p("setFC("+getFloat(fs)+"<"+getFloat(ft)+");");
                                 break;
-                                */
-                            default: throw new RuntimeException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+                            case 62: // C.LE.D
+                                p("setFC("+getFloat(fs)+"<="+getFloat(ft)+");");
+                                break;                                
+                            default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
                         }
                         break;
                     }
                     case 17: { // Double
                         switch(subcode) {
                             case 0: // ADD.D
-                                emit(pc, "setDouble(fd,"+getDouble(fs)+"+"+getDouble(ft)+");");
+                                p(setDouble(fd,getDouble(fs)+"+"+getDouble(ft)));
                                 break;
                             case 1: // SUB.D
-                                emit(pc, "setDouble(fd,"+getDouble(fs)+"-"+getDouble(ft)+");");
+                                p(setDouble(fd,getDouble(fs)+"-"+getDouble(ft)));
                                 break;
                             case 2: // MUL.D
-                                emit(pc, "setDouble(fd,"+getDouble(fs)+"*"+getDouble(ft)+");");
+                                p(setDouble(fd,getDouble(fs)+"*"+getDouble(ft)));
                                 break;
                             case 3: // DIV.D
-                                emit(pc, "setDouble(fd,"+getDouble(fs)+"/"+getDouble(ft)+");");
+                                p(setDouble(fd,getDouble(fs)+"/"+getDouble(ft)));
                                 break;
                             case 5: // ABS.D
-                                emit(pc, "setDouble(fd,Math.abs("+getDouble(fs)+"));");
+                                p(setDouble(fd,"Math.abs("+getDouble(fs)+")"));
                                 break;
                             case 6: // MOV.D
-                                emit(pc, "f"+fd+" = f"+fs+";");
-                                emit(pc, "f"+fd+1+" = f"+fs+1+";");
+                                p("f"+fd+" = f"+fs+";");
+                                p("f"+(fd+1)+" = f"+(fs+1)+";");
                                 break;
                             case 7: // NEG.D
-                                emit(pc, "setDouble(fd,-"+getDouble(fs)+");"); // FIXME: just flip the sign bit"
+                                p(setDouble(fd,"-"+getDouble(fs))); // FEATURE: just flip the sign bit
                                 break;
                             case 32: // CVT.S.D
-                                emit(pc, "setFloat(fd,(float)"+getDouble(fs)+");");
+                                p(setFloat(fd,"(float)"+getDouble(fs)));
                                 break;
                             case 36: // CVT.W.D
-                                switch(roundingMode()) {
-                                    case 0: emit(pc, "f"+fd+" = (int)Math.floor("+getDouble(fs)+"+0.5);"); break; // Round to nearest
-                                    case 1: emit(pc, "f"+fd+" = (int)"+getDouble(fs)+";"); break; // Round towards zero
-                                    case 2: emit(pc, "f"+fd+" = (int)Math.ceil("+getDouble(fs)+");"); break; // Round towards plus infinity
-                                    case 3: emit(pc, "f"+fd+" = (int)Math.floor("+getDouble(fs)+");"); break; // Round towards minus infinity
-                                }
+                                p("// CVT.W.D");
+                                p("switch(roundingMode()) {");
+                                    indent++;
+                                    p("case 0: f"+fd+" = (int)Math.floor("+getDouble(fs)+"+0.5); break; // Round to nearest");
+                                    p("case 1: f"+fd+" = (int)"+getDouble(fs)+"; break; // Round towards zero");
+                                    p("case 2: f"+fd+" = (int)Math.ceil("+getDouble(fs)+"); break; // Round towards plus infinity");
+                                    p("case 3: f"+fd+" = (int)Math.floor("+getDouble(fs)+"); break; // Round towards minus infinity");
+                                    indent--;
+                                p("}");
                                 break;
-                                /* FIXME not implemented yet
                             case 50: // C.EQ.D
-                                setFC("+getDouble(fs)+" == "+getDouble(ft)+"); // FIXME: just compare the ints, be sure things are normalized
+                                p("setFC("+getDouble(fs)+"=="+getDouble(ft)+");"); // FEATURE: just compare the ints, be sure things are normalized
                                 break;
                             case 60: // C.LT.D
-                                setFC("+getDouble(fs)+" < "+getDouble(ft)+");
+                                p("setFC("+getDouble(fs)+"<"+getDouble(ft)+");");
                                 break;
                             case 62: // C.LE.D
-                                setFC("+getDouble(fs)+" <= "+getDouble(ft)+");
+                                p("setFC("+getDouble(fs)+"<="+getDouble(ft)+");");
                                 break;                                
-                                */
-                            default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+                            default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
                         }
                         break;
                     }
                     case 20: { // Integer
                         switch(subcode) {
+                            case 32: // CVT.S.W
+                                p(" // CVS.S.W");
+                                p(setFloat(fd,"((float)f"+fs+")"));
+                                break;
                             case 33: // CVT.D.W
-                                emit(pc, "setDouble(fd,(double)f"+fs+");");
+                                p("// CVT.D.W");
+                                p(setDouble(fd,"((double)f"+fs+")"));
                                 break;
-                            default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+                            default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
                         }
-                        break;
+                        break; 
                     }
                     default:
-                        throw new RuntimeException("Invalid Instruction 17/" + rs);
+                        throw new CompilationException("Invalid Instruction 17/" + rs);
+                }
+                break;
             }
             case 18: case 19:
-                throw new RuntimeException("No coprocessor installed");
+                throw new CompilationException("coprocessor 2 and 3 instructions not available");
             case 32: { // LB
-                    /* FIXME not done yet 
-                emit(pc, "addr = r"+rs+" + "+signedImmediate+";");
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&3) {
-                    case 0: int tmp = (tmp>>>24)&0xff; break;
-                    case 1: int tmp = (tmp>>>16)&0xff; break;
-                    case 2: int tmp = (tmp>>> 8)&0xff; break;
-                    case 3: int tmp = (tmp>>> 0)&0xff; break;
-                }
-                if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend
-                r"+rt+" = tmp;
-                    */
-                break;
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: tmp = (tmp>>>24)&0xff; break;");
+                    p("case 1: tmp = (tmp>>>16)&0xff; break;");
+                    p("case 2: tmp = (tmp>>> 8)&0xff; break;");
+                    p("case 3: tmp = (tmp>>> 0)&0xff; break;");
+                    indent--;
+                p("}");
+                p("if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend");
+                p("r"+rt+" = tmp;");
+                break; 
             }
             case 33: { // LH
-                /* FIXME not done yet
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&2) {
-                    case 0: int tmp = (tmp>>>16)&0xffff; break;
-                    case 2: int tmp = (tmp>>> 0)&0xffff; break;
-                }
-                if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend
-                r"+rt+" = tmp;
-                break;              
-                */
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&2) {");
+                    indent++;
+                    p("case 0: tmp = (tmp>>>16)&0xffff; break;");
+                    p("case 2: tmp = (tmp>>> 0)&0xffff; break;");
+                    indent--;
+                p("}");
+                p("if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend");
+                p("r"+rt+" = tmp;");
+                break; 
             }
-                /* FIXME not done yet
             case 34: { // LWL;
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&3) {
-                    case 0: r"+rt+" = (r"+rt+"&0x00000000)|(tmp<< 0); break;
-                    case 1: r"+rt+" = (r"+rt+"&0x000000ff)|(tmp<< 8); break;
-                    case 2: r"+rt+" = (r"+rt+"&0x0000ffff)|(tmp<<16); break;
-                    case 3: r"+rt+" = (r"+rt+"&0x00ffffff)|(tmp<<24); break;
-                }
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: r"+rt+" = (r"+rt+"&0x00000000)|(tmp<< 0); break;");
+                    p("case 1: r"+rt+" = (r"+rt+"&0x000000ff)|(tmp<< 8); break;");
+                    p("case 2: r"+rt+" = (r"+rt+"&0x0000ffff)|(tmp<<16); break;");
+                    p("case 3: r"+rt+" = (r"+rt+"&0x00ffffff)|(tmp<<24); break;");
+                    indent--;
+                p("}");
                 break;
             }
             case 35: // LW
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    r"+rt+" = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    r"+rt+" = memRead(addr);
-                }
+                memRead("r" + rs +"+"+signedImmediate,"r"+rt);
                 break;
             case 36: { // LBU
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr);
-                }
-                switch(addr&3) {
-                    case 0: r"+rt+" = (tmp>>>24)&0xff; break;
-                    case 1: r"+rt+" = (tmp>>>16)&0xff; break;
-                    case 2: r"+rt+" = (tmp>>> 8)&0xff; break;
-                    case 3: r"+rt+" = (tmp>>> 0)&0xff; break;
-                }
-                break;
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: r"+rt+" = (tmp>>>24)&0xff; break;");
+                    p("case 1: r"+rt+" = (tmp>>>16)&0xff; break;");
+                    p("case 2: r"+rt+" = (tmp>>> 8)&0xff; break;");
+                    p("case 3: r"+rt+" = (tmp>>> 0)&0xff; break;");
+                    indent--;
+                p("}");
+                break; 
             }
             case 37: { // LHU
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&2) {
-                    case 0: r"+rt+" = (tmp>>>16)&0xffff; break;
-                    case 2: r"+rt+" = (tmp>>> 0)&0xffff; break;
-                }
-                break;
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&2) {");
+                    indent++;
+                    p("case 0: r"+rt+" = (tmp>>>16)&0xffff; break;");
+                    p("case 2: r"+rt+" = (tmp>>> 0)&0xffff; break;");
+                    indent--;
+                p("}");
+                break; 
             }
             case 38: { // LWR
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&3) {
-                    case 0: r"+rt+" = (r"+rt+"&0xffffff00)|(tmp>>>24); break;
-                    case 1: r"+rt+" = (r"+rt+"&0xffff0000)|(tmp>>>16); break;
-                    case 2: r"+rt+" = (r"+rt+"&0xff000000)|(tmp>>> 8); break;
-                    case 3: r"+rt+" = (r"+rt+"&0x00000000)|(tmp>>> 0); break;
-                }
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: r"+rt+" = (r"+rt+"&0xffffff00)|(tmp>>>24); break;");
+                    p("case 1: r"+rt+" = (r"+rt+"&0xffff0000)|(tmp>>>16); break;");
+                    p("case 2: r"+rt+" = (r"+rt+"&0xff000000)|(tmp>>> 8); break;");
+                    p("case 3: r"+rt+" = (r"+rt+"&0x00000000)|(tmp>>> 0); break;");
+                    indent--;
+                p("}");
                 break;
             }
             case 40: { // SB
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&3) {
-                    case 0: int tmp = (tmp&0x00ffffff) | ((r"+rt+"&0xff)<<24); break;
-                    case 1: int tmp = (tmp&0xff00ffff) | ((r"+rt+"&0xff)<<16); break;
-                    case 2: int tmp = (tmp&0xffff00ff) | ((r"+rt+"&0xff)<< 8); break;
-                    case 3: int tmp = (tmp&0xffffff00) | ((r"+rt+"&0xff)<< 0); break;
-                }
-                try {
-                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
-                } catch(RuntimeException e) {
-                    memWrite(addr&~3,tmp);
-                }
+                p("// SB");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: tmp = (tmp&0x00ffffff) | ((r"+rt+"&0xff)<<24); break;");
+                    p("case 1: tmp = (tmp&0xff00ffff) | ((r"+rt+"&0xff)<<16); break;");
+                    p("case 2: tmp = (tmp&0xffff00ff) | ((r"+rt+"&0xff)<< 8); break;");
+                    p("case 3: tmp = (tmp&0xffffff00) | ((r"+rt+"&0xff)<< 0); break;");
+                    indent--;
+                p("}");
+                memWrite("addr&~3","tmp");
                 break;
             }
             case 41: { // SH
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&2) {
-                    case 0: int tmp = (tmp&0x0000ffff) | ((r"+rt+"&0xffff)<<16); break;
-                    case 2: int tmp = (tmp&0xffff0000) | ((r"+rt+"&0xffff)<< 0); break;
-                }
-                try {
-                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
-                } catch(RuntimeException e) {
-                    memWrite(addr&~3,tmp);
-                }
+                p("// SH");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&2) {");
+                    indent++;
+                    p("case 0: tmp = (tmp&0x0000ffff) | ((r"+rt+"&0xffff)<<16); break;");
+                    p("case 2: tmp = (tmp&0xffff0000) | ((r"+rt+"&0xffff)<< 0); break;");
+                    indent--;
+                p("}");
+                memWrite("addr&~3","tmp");
                 break;
             }
             case 42: { // SWL
-                addr = r"+rs+" + signedImmediate;
-                int tmp = memRead(addr&~3);
-                switch(addr&3) {
-                    case 0: tmp=(tmp&0x00000000)|(r"+rt+">>> 0); break;
-                    case 1: tmp=(tmp&0xff000000)|(r"+rt+">>> 8); break;
-                    case 2: tmp=(tmp&0xffff0000)|(r"+rt+">>>16); break;
-                    case 3: tmp=(tmp&0xffffff00)|(r"+rt+">>>24); break;
-                }
-                try {
-                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
-                } catch(RuntimeException e) {
-                    memWrite(addr&~3,tmp);
-                }
+                p(" // SWL");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: tmp=(tmp&0x00000000)|(r"+rt+">>> 0); break;");
+                    p("case 1: tmp=(tmp&0xff000000)|(r"+rt+">>> 8); break;");
+                    p("case 2: tmp=(tmp&0xffff0000)|(r"+rt+">>>16); break;");
+                    p("case 3: tmp=(tmp&0xffffff00)|(r"+rt+">>>24); break;");
+                    indent--;
+                p("}");
+                memWrite("addr&~3","tmp");
                 break;
             }
             case 43: // SW
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = r"+rt+";
-                } catch(RuntimeException e) {
-                    memWrite(addr&~3,r"+rt+");
-                }
+                memWrite("r"+rs+"+"+signedImmediate,"r" + rt);
                 break;
             case 46: { // SWR
-                addr = r"+rs+" + signedImmediate;
-                int tmp = memRead(addr&~3);
-                switch(addr&3) {
-                    case 0: tmp=(tmp&0x00ffffff)|(r"+rt+"<<24); break;
-                    case 1: tmp=(tmp&0x0000ffff)|(r"+rt+"<<16); break;
-                    case 2: tmp=(tmp&0x000000ff)|(r"+rt+"<< 8); break;
-                    case 3: tmp=(tmp&0x00000000)|(r"+rt+"<< 0); break;
-                }
-                memWrite(addr&~3,tmp);
+                p(" // SWR");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: tmp=(tmp&0x00ffffff)|(r"+rt+"<<24); break;");
+                    p("case 1: tmp=(tmp&0x0000ffff)|(r"+rt+"<<16); break;");
+                    p("case 2: tmp=(tmp&0x000000ff)|(r"+rt+"<< 8); break;");
+                    p("case 3: tmp=(tmp&0x00000000)|(r"+rt+"<< 0); break;");
+                    indent--;
+                p("}");
+                memWrite("addr&~3","tmp");
                 break;
             }
             case 49: // LWC1
-                f"+rt+" = memRead(r"+rs+" + signedImmediate);
+                memRead("r"+rs+"+"+signedImmediate,"f"+rt);
                 break;
             case 57: // SWC1
-                memWrite(r"+rs+" + signedImmediate,f"+rt+");
+                memWrite("r"+rs+"+"+signedImmediate,"f"+rt);
                 break;
             default:
-                throw new EmulationException("Invalid Instruction: " + op);
-                */
+                throw new CompilationException("Invalid Instruction: " + op + " at " + toHex(pc));
         }
-        
     }
     
-    static String prefix = "";
-    static void emit(int vaddr, String s) {
-        if (s.indexOf("r0 = ") != -1) s = "    /* NOP */";
-        if (!s.trim().endsWith("return;") && s.indexOf("throw") == -1) s += " pc = 0x" + Long.toString((vaddr + 4) & 0xffffffffL,16) + ";";
-        System.out.println(s);
+    private static void memWrite(String addr, String target) {
+        if(fastMem)
+            p("writePages[("+addr+")>>>"+Runtime.PAGE_SHIFT+"][(("+addr+")>>>2)&"+toHex(Runtime.PAGE_WORDS-1)+"] = " + target + ";");
+        else
+            p("memWrite(" + addr + "," + target + ");");
+            
+    }
+    
+    private static void memRead(String addr, String target) {
+        if(fastMem)
+            p(target  + " = readPages[("+addr+")>>>"+Runtime.PAGE_SHIFT+"][(("+addr+")>>>2)&"+toHex(Runtime.PAGE_WORDS-1)+"];");
+        else
+            p(target + " = memRead(" + addr + ");");
     }
     
-    static String getFloat(int r) { return "Float.intBitsToFloat(f"+r+")"; }
-    static String getDouble(int r) {
-        return "Double.longBitsToDouble(((f"+r+"&0xffffffffL) << 32) | (f"+r+"&0xffffffffL));";
+    private static String getFloat(int r) { return "(Float.intBitsToFloat(f"+r+"))"; }
+    private static String getDouble(int r) {
+        return "(Double.longBitsToDouble(((f"+(r+1)+"&0xffffffffL) << 32) | (f"+r+"&0xffffffffL)))";
     }
-    static String setFloat(int r, String expr) { return "f"+r+" = Float.floatToRawIntBits("+expr+");" }
-    static String setDouble(int r, String expr) {
+    private static String setFloat(int r, String expr) { return "f"+r+"=Float.floatToRawIntBits("+expr+");"; }
+    private static String setDouble(int r, String expr) {
         return "{ long l = Double.doubleToLongBits("+expr+"); "+
-            "f"+(r+1)+" = (int)(l >>> 32); f"+(r)+" = (int)l; }";
+            "f"+(r+1)+" = (int)(l >>> 32); f"+r+" = (int)l; }";
+    }
+    
+    private final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
+    private final static String toHex8(int n) {
+        String s = Long.toString(n & 0xffffffffL, 16);
+        StringBuffer sb = new StringBuffer("0x");
+        for(int i=8-s.length();i>0;i--) sb.append('0');
+        sb.append(s);
+        return sb.toString();
     }
 }
 
index 91dd79d..12821f0 100644 (file)
@@ -1,7 +1,7 @@
 package org.xwt.mips;
 import java.io.*;
 
-class ELF {
+public class ELF {
 
     private DataInput fd;
     private Object image;
@@ -19,12 +19,10 @@ class ELF {
     public PHeader[] pheaders;
     public SHeader[] sheaders;
     
+    private byte[] stringTable;
+    
     private boolean sectionReaderActive;
     
-    private static void skipFully(DataInput fd, int n) throws IOException {
-        while(n>0) n -= fd.skipBytes(n);
-    }
-
     public class ELFHeader {
         byte klass;
         byte data;
@@ -52,10 +50,10 @@ class ELF {
             if(fd.readInt() != ELF_MAGIC) throw new ELFException("Bad Magic (is: " );
             klass = fd.readByte();
             data = fd.readByte();
-            skipFully(fd, 1); // version
+            fd.skipBytes(1); // version
             osabi = fd.readByte();
             abiversion = fd.readByte();
-            skipFully(fd, 7);
+            fd.skipBytes(7);
             type = fd.readShort();
             machine = fd.readShort();
             version = fd.readInt();
@@ -101,11 +99,10 @@ class ELF {
         }
         
         public boolean writable() { return (flags & PF_W) != 0; }
-        public boolean executable() { return (flags & PF_X) != 0; }
         
         public InputStream getInputStream() throws IOException {
-            return new BufferedInputStream(new SectionInputStream(
-                offset,offset+filesz));
+                return new BufferedInputStream(new SectionInputStream(
+                        offset,offset+filesz));
         }
     }
     
@@ -122,7 +119,9 @@ class ELF {
         public int addralign;
         public int entsize;
         
-        public static final int T_NOBITS = 8;
+        public static final int SHT_SYMTAB = 2;
+        public static final int SHT_STRTAB = 3;
+        public static final int SHT_NOBITS = 8;
         
         SHeader() throws IOException {
             nameidx = fd.readInt();
@@ -138,12 +137,13 @@ class ELF {
         }
         
         public InputStream getInputStream() throws IOException {
-            return new BufferedInputStream(new SectionInputStream(
-                offset, type == T_NOBITS ? 0 : offset+size));
+                return new BufferedInputStream(new SectionInputStream(
+                        offset, type == SHT_NOBITS ? 0 : offset+size));
         }
     }
     
     public ELF(Object img) throws IOException, ELFException {
+        if (img instanceof String) image = new MyRandomAccessFile((String)img,"r");
         image = img;
         seek(0);
         header = new ELFHeader();
@@ -159,16 +159,22 @@ class ELF {
         }
         if(header.shstrndx < 0 || header.shstrndx >= header.shnum) throw new ELFException("Bad shstrndx");
         seek(sheaders[header.shstrndx].offset);
-        byte[] a = new byte[sheaders[header.shstrndx].size];
-        fd.readFully(a);
+        stringTable = new byte[sheaders[header.shstrndx].size];
+        fd.readFully(stringTable);
+        
         for(int i=0;i<header.shnum;i++) {
             SHeader s = sheaders[i];
-            StringBuffer sb = new StringBuffer();
-               for(int off = s.nameidx;off < a.length && a[off] != 0; off++) sb.append((char)a[off]);
-            s.name = sb.toString();
-        }            
+            s.name = getString(s.nameidx);
+        }
     }
-        
+    
+    private String getString(int off) { return getString(off,stringTable); }
+    private String getString(int off,byte[] strtab) {
+        StringBuffer sb = new StringBuffer();
+        while(off < strtab.length && strtab[off] != 0) sb.append((char)strtab[off++]);
+        return sb.toString();
+    }
+    
     public SHeader sectionWithName(String name) {
         for(int i=0;i<sheaders.length;i++)
             if(sheaders[i].name.equals(name))
@@ -178,6 +184,10 @@ class ELF {
     
     public class ELFException extends IOException { ELFException(String s) { super(s); } }
     
+    private class MyRandomAccessFile extends RandomAccessFile  {
+        MyRandomAccessFile(String f,String m) throws FileNotFoundException { super(f,m); }
+    }
+    
     private class SectionInputStream extends InputStream {
         private int pos;
         private int maxpos;
@@ -191,29 +201,73 @@ class ELF {
         }
         
         private int bytesLeft() { return maxpos - pos; }
-        public int read() throws IOException {
-            if(bytesLeft()==0) return -1;
-            try {
-                byte b = fd.readByte();
-                pos++;
-                return b;
-            } catch (EOFException e) {
-                return -1;
-            }
-        }
+        public int read() throws IOException { if(bytesLeft()==0) return -1; int b = fd.readByte(); if(b >= 0) pos++; return b; }
         public int read(byte[] b, int off, int len) throws IOException {
-            fd.readFully(b, off, len);
-            return len;
+            fd.readFully(b,off,Math.min(len,bytesLeft())); return len;
         }
         public void close() { sectionReaderActive = false; }
     }
     
+    private Symtab _symtab;
+    public Symtab getSymtab() throws IOException {
+        if(_symtab != null) return _symtab;
+        
+        SHeader sh = sectionWithName(".symtab");
+        if(sh == null || sh.type != SHeader.SHT_SYMTAB) return null;
+        
+        SHeader sth = sectionWithName(".strtab");
+        if(sth == null || sth.type != SHeader.SHT_STRTAB) return null;
+        
+        byte[] strtab = new byte[sth.size];
+        DataInputStream dis = new DataInputStream(sth.getInputStream());
+        dis.readFully(strtab);
+        dis.close();
+        
+        return _symtab = new Symtab(sh.getInputStream(),sh.size,strtab);
+    }
+    
+    public class  Symtab {
+        public Symbol[] symbols;
+        
+        Symtab(InputStream is, int size, byte[] strtab) throws IOException {
+            DataInputStream dis = new DataInputStream(is);
+            int count = size/16;
+            symbols = new Symbol[count];
+            for(int i=0;i<count;i++) symbols[i] = new Symbol(dis,strtab);
+            dis.close();
+        }
+    }
+    
+    public class Symbol {
+        public String name;
+        public int addr;
+        public int size;
+        public byte info;
+        public byte type;
+        public byte other;
+        public SHeader sheader;
+        
+        public final static int STT_FUNC = 2;
+        
+        Symbol(DataInputStream dis, byte[] strtab) throws IOException {
+            name = getString(dis.readInt(),strtab);
+            addr = dis.readInt();
+            size = dis.readInt();
+            info = dis.readByte();
+            type = (byte)(info&0xf);
+            other = dis.readByte();
+            // FIXME: Find sheader entry
+            dis.readShort();
+        }
+    }
+    
     private static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
     
     public static void main(String[] args) throws IOException {
         ELF elf = new ELF(args[0]);
         System.out.println("Type: " + toHex(elf.header.type));
         System.out.println("Machine: " + toHex(elf.header.machine));
+        System.out.println("Entry: " + toHex(elf.header.entry));
         for(int i=0;i<elf.pheaders.length;i++) {
             ELF.PHeader ph = elf.pheaders[i];
             System.out.println("PHeader " + toHex(i));
@@ -231,5 +285,6 @@ class ELF {
             System.out.println("\tSize: " + sh.size);
             System.out.println("\tType: " + toHex(sh.type));
         }
+        Symtab symtab = elf.getSymtab();
     }
 }
diff --git a/src/org/xwt/mips/Errno.java b/src/org/xwt/mips/Errno.java
new file mode 100644 (file)
index 0000000..f718291
--- /dev/null
@@ -0,0 +1,121 @@
+package org.xwt.mips;
+public 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;
+}
index a957297..9b0f11b 100644 (file)
@@ -1,21 +1,19 @@
-// Copyright 2003 Adam Megacz
-// Author Brian Alliet
-// Based on org.xwt.mips.Compiler by Adam Megacz
-
+// 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 class Interpreter extends VM {
+import java.io.*;
 
+public class Interpreter extends Runtime {
     // Registers
     private int[] registers = new int[32];
     private int hi,lo;
     
     // Floating Point Registers
     private int[] fpregs = new int[32];
-
     // 24-31 - unused
-    // 23    - conditional bit
+    // 23 - conditional bit
     // 18-22 - unused
     // 12-17 - cause bits (unimplemented)
     // 7-11  - enables bits (unimplemented)
@@ -25,9 +23,6 @@ public class Interpreter extends VM {
     
     private int nextPC;
     
-    // The filename if the binary we're running
-    private Object image;
-    
     // Register Operations
     private final void setFC(boolean b) { fcsr = (fcsr&~0x800000) | (b ? 0x800000 : 0x000000); }
     private final int roundingMode() { return fcsr & 3; /* bits 0-1 */ }
@@ -41,19 +36,14 @@ public class Interpreter extends VM {
     private final float getFloat(int r) { return Float.intBitsToFloat(fpregs[r]); }
     private final void setFloat(int r, float f) { fpregs[r] = Float.floatToRawIntBits(f); }
     
-    // Main run loop
-    public void execute() throws EmulationException {
-        int[] r = registers;
-        if(state == PAUSED) state = RUNNING;
-        if(state != RUNNING) throw new IllegalStateException("execute() called in inappropriate state");
-        runSome(nextPC);
-    }
+    protected void _execute() throws ExecutionException { runSome(); }
     
-    // Main interpretor (also used for compilation)
+    // Main interpretor
     // the return value is meaningless, its just to catch people typing "return" by accident
-    private int runSome(int pc) throws FaultException,EmulationException {
+    private final int runSome() throws FaultException,ExecutionException {
         int[] r = registers;
         int[] f = fpregs;
+        int pc = nextPC;
         int nextPC = pc + 4;
     try {
     OUTER: for(;;) {
@@ -96,15 +86,14 @@ public class Interpreter extends VM {
                     case 3: // SRA
                         r[rd] = r[rt] >> shamt;
                         break;
-                    // FIXME: Do we need % 32 on the r[rs] ?
                     case 4: // SLLV
-                        r[rd] = r[rt] << r[rs];
+                        r[rd] = r[rt] << (r[rs]&0x1f);
                         break;
                     case 6: // SRLV
-                        r[rd] = r[rt] >>> r[rs];
+                        r[rd] = r[rt] >>> (r[rs]&0x1f);
                         break;
                     case 7: // SRAV
-                        r[rd] = r[rt] >> r[rs];
+                        r[rd] = r[rt] >> (r[rs]&0x1f);
                         break;
                     case 8: // JR
                         tmp = r[rs]; pc += 4; nextPC = tmp;
@@ -120,7 +109,7 @@ public class Interpreter extends VM {
                         }
                         break;
                     case 13: // BREAK
-                        throw new EmulationException("Break");
+                        throw new ExecutionException("Break");
                     case 16: // MFHI
                         r[rd] = hi;
                         break;
@@ -154,14 +143,18 @@ public class Interpreter extends VM {
                         lo = (int)((r[rs] & 0xffffffffL) / (r[rt] & 0xffffffffL));
                         break;
                     case 32: // ADD
-                        r[rd] = r[rs] + r[rt]; // FIXME: Trap on overflow
-                        break;
+                        throw new ExecutionException("ADD (add with oveflow trap) not suported");
+                        /*This must trap on overflow
+                        r[rd] = r[rs] + r[rt];
+                        break;*/
                     case 33: // ADDU
                         r[rd] = r[rs] + r[rt];
                         break;
                     case 34: // SUB
-                        r[rd] = r[rs] - r[rt]; // FIXME: Trap on overflow
-                        break;
+                        throw new ExecutionException("SUB (sub with oveflow trap) not suported");
+                        /*This must trap on overflow
+                        r[rd] = r[rs] - r[rt];
+                        break;*/
                     case 35: // SUBU
                         r[rd] = r[rs] - r[rt];
                         break;
@@ -184,7 +177,7 @@ public class Interpreter extends VM {
                         r[rd] = ((r[rs] & 0xffffffffL) < (r[rt] & 0xffffffffL)) ? 1 : 0;
                         break;
                     default:
-                        throw new EmulationException("Illegal instruction 0/" + subcode);
+                        throw new ExecutionException("Illegal instruction 0/" + subcode);
                 }
                 break;
             }
@@ -215,7 +208,7 @@ public class Interpreter extends VM {
                         }
                         break;
                     default:
-                        throw new EmulationException("Illegal Instruction");
+                        throw new ExecutionException("Illegal Instruction");
                 }
                 break;
             }
@@ -278,29 +271,29 @@ public class Interpreter extends VM {
                 r[rt] = unsignedImmediate << 16;
                 break;
             case 16:
-                throw new EmulationException("TLB/Exception support not implemented");
+                throw new ExecutionException("TLB/Exception support not implemented");
             case 17: { // FPU
                 boolean debug = false;
-                String line = debug ? sourceLine(pc) : "";
+                String line = "";
                 boolean debugon = debug && (line.indexOf("dtoa.c:51") >= 0 || line.indexOf("dtoa.c:52") >= 0 || line.indexOf("test.c") >= 0);
                 if(rs > 8 && debugon)
                     System.out.println("               FP Op: " + op + "/" + rs + "/" + subcode + " " + line);
-                // FIXME: This could probably be removed. I don't think gcc will ever generate code that does this
+                // FEATURE: This could probably be removed. I don't think gcc will ever generate code that does this
                 if(roundingMode() != 0 && rs != 6 /*CTC.1*/ && !((rs==16 || rs==17) && subcode == 36 /* CVT.W.Z */))
-                    throw new EmulationException("Non-cvt.w.z operation attempted with roundingMode != round to nearest");
+                    throw new ExecutionException("Non-cvt.w.z operation attempted with roundingMode != round to nearest");
                 switch(rs) {
                     case 0: // MFC.1
                         r[rt] = f[rd];
                         break;
                     case 2: // CFC.1
-                        if(fs != 31) throw new EmulationException("FCR " + fs + " unavailable");
+                        if(fs != 31) throw new ExecutionException("FCR " + fs + " unavailable");
                         r[rt] = fcsr;
                         break;
                     case 4: // MTC.1
                         f[rd] = r[rt];
                         break;
                     case 6: // CTC.1
-                        if(fs != 31) throw new EmulationException("FCR " + fs + " unavailable");
+                        if(fs != 31) throw new ExecutionException("FCR " + fs + " unavailable");
                         fcsr = r[rt];   
                         break;
                     case 8: // BC1F, BC1T
@@ -330,7 +323,7 @@ public class Interpreter extends VM {
                                 f[fd] = f[fs];
                                 break;
                             case 7: // NEG.S
-                                setFloat(fd,-getFloat(fs)); // FIXME: just flip the sign bit
+                                setFloat(fd,-getFloat(fs)); // FEATURE: just flip the sign bit
                                 break;
                             case 33: // CVT.D.S
                                 setDouble(fd,getFloat(fs));
@@ -344,12 +337,12 @@ public class Interpreter extends VM {
                                 }
                                 break;
                             case -50: // C.EQ.S
-                                setFC(getFloat(fs) == getFloat(ft)); // FIXME: just compare the ints, be sure things are normalized
+                                setFC(getFloat(fs) == getFloat(ft)); // FEATURE: just compare the ints, be sure things are normalized
                                 break;
                             case 60: // C.LT.S
                                 setFC(getFloat(fs) < getFloat(ft));
                                 break;
-                            default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+                            default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode);
                         }
                         break;
                     }
@@ -378,7 +371,7 @@ public class Interpreter extends VM {
                                 f[fd+1] = f[fs+1];
                                 break;
                             case 7: // NEG.D
-                                setDouble(fd,-getDouble(fs)); // FIXME: just flip the sign bit
+                                setDouble(fd,-getDouble(fs)); // FEATURE: just flip the sign bit
                                 break;
                             case 32: // CVT.S.D
                                 setFloat(fd,(float)getDouble(fs));
@@ -394,7 +387,7 @@ public class Interpreter extends VM {
                                 if(debugon) System.out.println("CVT.W.D: f" + fd + ":" + f[fd]);
                                 break;
                             case 50: // C.EQ.D
-                                setFC(getDouble(fs) == getDouble(ft)); // FIXME: just compare the ints, be sure things are normalized
+                                setFC(getDouble(fs) == getDouble(ft)); // FEATURE: just compare the ints, be sure things are normalized
                                 break;
                             case 60: // C.LT.D
                                 setFC(getDouble(fs) < getDouble(ft));
@@ -402,7 +395,7 @@ public class Interpreter extends VM {
                             case 62: // C.LE.D
                                 setFC(getDouble(fs) <= getDouble(ft));
                                 break;                                
-                            default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+                            default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode);
                         }
                         break;
                     }
@@ -411,21 +404,21 @@ public class Interpreter extends VM {
                             case 33: // CVT.D.W
                                 setDouble(fd,(double)f[fs]);
                                 break;
-                            default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+                            default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode);
                         }
                         break;
                     }
                     default:
-                        throw new EmulationException("Invalid Instruction 17/" + rs);
+                        throw new ExecutionException("Invalid Instruction 17/" + rs);
                 }
                 break;
             }
             case 18: case 19:
-                throw new EmulationException("No coprocessor installed");
+                throw new ExecutionException("No coprocessor installed");
             case 32: { // LB
                 addr = r[rs] + signedImmediate;
                 try {
-                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
                 } catch(RuntimeException e) {
                     tmp = memRead(addr&~3);
                 }
@@ -442,7 +435,7 @@ public class Interpreter extends VM {
             case 33: { // LH
                 addr = r[rs] + signedImmediate;
                 try {
-                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
                 } catch(RuntimeException e) {
                     tmp = memRead(addr&~3);
                 }
@@ -457,7 +450,7 @@ public class Interpreter extends VM {
             case 34: { // LWL;
                 addr = r[rs] + signedImmediate;
                 try {
-                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
                 } catch(RuntimeException e) {
                     tmp = memRead(addr&~3);
                 }
@@ -472,7 +465,7 @@ public class Interpreter extends VM {
             case 35: // LW
                 addr = r[rs] + signedImmediate;
                 try {
-                    r[rt] = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+                    r[rt] = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
                 } catch(RuntimeException e) {
                     r[rt] = memRead(addr);
                 }
@@ -480,7 +473,7 @@ public class Interpreter extends VM {
             case 36: { // LBU
                 addr = r[rs] + signedImmediate;
                 try {
-                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
                 } catch(RuntimeException e) {
                     tmp = memRead(addr);
                 }
@@ -495,7 +488,7 @@ public class Interpreter extends VM {
             case 37: { // LHU
                 addr = r[rs] + signedImmediate;
                 try {
-                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
                 } catch(RuntimeException e) {
                     tmp = memRead(addr&~3);
                 }
@@ -508,7 +501,7 @@ public class Interpreter extends VM {
             case 38: { // LWR
                 addr = r[rs] + signedImmediate;
                 try {
-                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
                 } catch(RuntimeException e) {
                     tmp = memRead(addr&~3);
                 }
@@ -523,7 +516,7 @@ public class Interpreter extends VM {
             case 40: { // SB
                 addr = r[rs] + signedImmediate;
                 try {
-                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
                 } catch(RuntimeException e) {
                     tmp = memRead(addr&~3);
                 }
@@ -534,7 +527,7 @@ public class Interpreter extends VM {
                     case 3: tmp = (tmp&0xffffff00) | ((r[rt]&0xff)<< 0); break;
                 }
                 try {
-                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
+                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
                 } catch(RuntimeException e) {
                     memWrite(addr&~3,tmp);
                 }
@@ -543,7 +536,7 @@ public class Interpreter extends VM {
             case 41: { // SH
                 addr = r[rs] + signedImmediate;
                 try {
-                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+                    tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
                 } catch(RuntimeException e) {
                     tmp = memRead(addr&~3);
                 }
@@ -552,7 +545,7 @@ public class Interpreter extends VM {
                     case 2: tmp = (tmp&0xffff0000) | ((r[rt]&0xffff)<< 0); break;
                 }
                 try {
-                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
+                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
                 } catch(RuntimeException e) {
                     memWrite(addr&~3,tmp);
                 }
@@ -568,7 +561,7 @@ public class Interpreter extends VM {
                     case 3: tmp=(tmp&0xffffff00)|(r[rt]>>>24); break;
                 }
                 try {
-                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
+                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
                 } catch(RuntimeException e) {
                     memWrite(addr&~3,tmp);
                 }
@@ -577,7 +570,7 @@ public class Interpreter extends VM {
             case 43: // SW
                 addr = r[rs] + signedImmediate;
                 try {
-                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = r[rt];
+                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = r[rt];
                 } catch(RuntimeException e) {
                     memWrite(addr&~3,r[rt]);
                 }
@@ -601,12 +594,12 @@ public class Interpreter extends VM {
                 memWrite(r[rs] + signedImmediate,f[rt]);
                 break;
             default:
-                throw new EmulationException("Invalid Instruction: " + op);
+                throw new ExecutionException("Invalid Instruction: " + op);
         }
         pc = nextPC;
         nextPC = pc + 4;
     } // for(;;)
-    } catch(EmulationException e) {
+    } catch(ExecutionException e) {
         this.nextPC = pc;
         throw e;
     }
@@ -615,9 +608,7 @@ public class Interpreter extends VM {
     
     // Image loading function
     void loadImage(Object file) throws IOException {
-        ELF elf = null;
-        if (file instanceof String) elf = new ELF(new RandomAccessFile((String)file,"r"));
-        else if (file instanceof byte[]) elf = new ELF((byte[])file);
+        ELF elf = new ELF(file);
         if(elf.header.type != ELF.ELFHeader.ET_EXEC)
             throw new IOException("Binary is not an executable");
         if(elf.header.machine != ELF.ELFHeader.EM_MIPS)
@@ -653,41 +644,25 @@ public class Interpreter extends VM {
                 dis.close();
             }
         }
-        image = file;
         state = INITIALIZED;
     }
     
     protected void _start(int pc) {
+                registers[K0] = STUFF_BASE;
+                registers[K1] = PAGE_SIZE;
         registers[SP] = INITIAL_SP;
         registers[RA] = 0xdeadbeef;
         nextPC = pc;
     }
-    public Interpreter() { }
-    public Interpreter(String image) throws IOException { loadImage(image); }
-    public Interpreter(byte[] image) throws IOException { loadImage(image); }
-    
-    // Debug functions
-    // NOTE: This probably requires a jdk > 1.1, however, it is only used for debugging
-    public String sourceLine(int pc) {
-        final String addr2line = "mips-unknown-elf-addr2line";
-        String line;
-        if(image==null) return null;
-        try {
-            Process p = Runtime.getRuntime().exec(new String[]{addr2line,"-e",image.toString(),toHex(pc)});
-            line = new BufferedReader(new InputStreamReader(p.getInputStream())).readLine();
-            if(line == null) return null;
-            while(line.startsWith("../")) line = line.substring(3);
-            return line;
-        } catch(IOException e) {
-            return null;
-        }
-    }
+    public Interpreter() { super(true); /* allow empty pages */ }
+    public Interpreter(String filename) throws IOException { loadImage(filename); }
+    public Interpreter(byte[] bytes) throws IOException { loadImage(bytes); }
     
     public class DebugShutdownHook implements Runnable {
         public void run() {
             int pc = nextPC;
             if(getState() == RUNNING)
-                System.err.print("\nCPU Executing " + toHex(pc) + ": " + sourceLine(pc) + "\n");
+                System.err.print("\nCPU Executing " + toHex(pc) + "\n");
         }
     }
 
@@ -695,7 +670,7 @@ public class Interpreter extends VM {
         String image = argv[0];
         Interpreter emu = new Interpreter();
         emu.loadImage(image);
-        Runtime.getRuntime().addShutdownHook(new Thread(emu.new DebugShutdownHook()));
+        java.lang.Runtime.getRuntime().addShutdownHook(new Thread(emu.new DebugShutdownHook()));
         // User data
         int addr = emu.sbrk(PAGE_SIZE);
         for(int i=0;i<10;i++) {
@@ -711,5 +686,3 @@ public class Interpreter extends VM {
         System.exit(status);
     }
 }
-
-
diff --git a/src/org/xwt/mips/Registers.java b/src/org/xwt/mips/Registers.java
new file mode 100644 (file)
index 0000000..79603c4
--- /dev/null
@@ -0,0 +1,41 @@
+package org.xwt.mips;
+interface Registers {
+    // Register Names
+    public final static int ZERO = 0; // Immutable, hardwired to 0
+    public final static int AT = 1;  // Reserved for assembler
+    public final static int K0 = 26; // Reserved for kernel 
+    public final static int K1 = 27; // Reserved for kernel 
+    public final static int GP = 28; // Global pointer (the middle of .sdata/.sbss)
+    public final static int SP = 29; // Stack pointer
+    public final static int FP = 30; // Frame Pointer
+    public final static int RA = 31; // Return Address
+    
+    // Return values (caller saved)
+    public final static int V0 = 2;
+    public final static int V1 = 3;
+    // Argument Registers (caller saved)
+    public final static int A0 = 4; 
+    public final static int A1 = 5;
+    public final static int A2 = 6;
+    public final static int A3 = 7;
+    // Temporaries (caller saved)
+    public final static int T0 = 8;
+    public final static int T1 = 9;
+    public final static int T2 = 10;
+    public final static int T3 = 11;
+    public final static int T4 = 12;
+    public final static int T5 = 13;
+    public final static int T6 = 14;
+    public final static int T7 = 15;
+    public final static int T8 = 24;
+    public final static int T9 = 25;
+    // Saved (callee saved)
+    public final static int S0 = 16;
+    public final static int S1 = 17;
+    public final static int S2 = 18;
+    public final static int S3 = 19;
+    public final static int S4 = 20;
+    public final static int S5 = 21;
+    public final static int S6 = 22;
+    public final static int S7 = 23;
+}
similarity index 51%
rename from src/org/xwt/mips/VM.java
rename to src/org/xwt/mips/Runtime.java
index b0bbf7b..89918a7 100644 (file)
+// 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;
@@ -125,7 +177,7 @@ public abstract class VM implements Syscalls, Errno {
             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);
@@ -134,17 +186,19 @@ public abstract class VM implements Syscalls, Errno {
         }
         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;
@@ -158,7 +212,7 @@ public abstract class VM implements Syscalls, Errno {
             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;
@@ -166,13 +220,14 @@ public abstract class VM implements Syscalls, Errno {
         }
         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;
@@ -182,7 +237,7 @@ public abstract class VM implements Syscalls, Errno {
         } 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) {
@@ -190,7 +245,8 @@ public abstract class VM implements Syscalls, Errno {
         }
     }
     
-    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);
@@ -199,7 +255,7 @@ public abstract class VM implements Syscalls, Errno {
         } 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) {
@@ -207,45 +263,52 @@ public abstract class VM implements Syscalls, Errno {
         }
     }
     
-    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++) {
@@ -254,18 +317,38 @@ public abstract class VM implements Syscalls, Errno {
         }
     }
     
-    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];
@@ -275,9 +358,69 @@ public abstract class VM implements Syscalls, Errno {
         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;
@@ -301,7 +444,8 @@ public abstract class VM implements Syscalls, Errno {
         }
     }
 
-    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 {
@@ -324,7 +468,8 @@ public abstract class VM implements Syscalls, Errno {
         }
     }
     
-    private int close(int fdn) {
+    /** The close syscall */
+    private int sys_close(int fdn) {
         FileDescriptor fd;
         try {
             fd = fds[fdn];
@@ -337,19 +482,38 @@ public abstract class VM implements Syscalls, Errno {
         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
@@ -362,7 +526,8 @@ public abstract class VM implements Syscalls, Errno {
         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];
@@ -373,6 +538,8 @@ public abstract class VM implements Syscalls, Errno {
         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;
@@ -381,74 +548,19 @@ public abstract class VM implements Syscalls, Errno {
             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) {
@@ -471,39 +583,47 @@ public abstract class VM implements Syscalls, Errno {
                 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(;;) {
@@ -516,26 +636,9 @@ public abstract class VM implements Syscalls, Errno {
             }
         }
     }
-     
-    // 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;
@@ -545,7 +648,8 @@ public abstract class VM implements Syscalls, Errno {
         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; }
@@ -554,27 +658,42 @@ public abstract class VM implements Syscalls, Errno {
         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;
@@ -589,19 +708,16 @@ public abstract class VM implements Syscalls, Errno {
             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; }
@@ -623,154 +739,33 @@ public abstract class VM implements Syscalls, Errno {
         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;
-}
diff --git a/src/org/xwt/mips/Syscalls.java b/src/org/xwt/mips/Syscalls.java
new file mode 100644 (file)
index 0000000..26829df
--- /dev/null
@@ -0,0 +1,16 @@
+package org.xwt.mips;
+public 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;
+}
index 374cf25..5b8ab8b 100644 (file)
@@ -1,7 +1,4 @@
-SEARCH_DIR(build)
 ENTRY(_start)
-STARTUP(org/xwt/mips/crt0.c.o)
-INPUT(org/xwt/mips/syscalls.c.o)
 GROUP(-lc -lgcc)
 __DYNAMIC  =  0;
 
@@ -9,14 +6,36 @@ SECTIONS {
   . = 0x10000;
   .text : {
     *(.init)
-    *(.text) *(.rodata) *(.rodata.*) *(.eh_frame)
+    *(.text) *(.text.*) 
     *(.fini)
   }
 
+  .ctors :
+  {
+    KEEP (*crtbegin.o(.ctors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  }
+  
+  .dtors :
+  {
+    KEEP (*crtbegin.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  }
+
   . = ALIGN(4k);
+
+  .rodata : {
+    *(.rodata) *(.rodata.*) *(.eh_frame) *(.jcr)
+  }
+
   .data : {
     *(.data)
   }
+
   . = ALIGN(16);
    PROVIDE(_gp = . + 0x8000);
   .sdata : {
index 0726fda..3079606 100644 (file)
@@ -13,8 +13,8 @@ public class Freetype {
     public Freetype() { }
 
     private static byte[] image = null;
-    private static final int FONT_RESERVED = 256*1024;
 
+    private int mem_allocated = 0;
     private org.xwt.mips.Interpreter vm = null;
 
     private Res loadedStream = null;
@@ -27,7 +27,7 @@ public class Freetype {
             byte[] fontstream = InputStreamToByteArray.convert(is);
             if (image == null) image = InputStreamToByteArray.convert(Main.builtin.getInputStream("freetype.mips"));
             vm = new org.xwt.mips.Interpreter(image);
-            int baseAddr = vm.sbrk(FONT_RESERVED);
+            int baseAddr = vm.sbrk(fontstream.length);
             vm.copyout(fontstream, baseAddr, fontstream.length);
             vm.setUserInfo(0, baseAddr);
             vm.setUserInfo(1, fontstream.length);
@@ -42,35 +42,8 @@ public class Freetype {
         int width = 0;
         int height = 0;
         byte[] data = null;
-        File cacheFile = null;
-        /*
+
         try {
-            String key = glyph.font.res.getCacheKey() + ":" + glyph.c;
-            key = new String(Base64.encode(key.getBytes()));
-            cacheFile = new java.io.File(System.getProperty("user.home") +
-                                         java.io.File.separatorChar + ".xwt" +
-                                         java.io.File.separatorChar + "caches" +
-                                         java.io.File.separatorChar + "glyphs" +
-                                         java.io.File.separatorChar +
-                                         key);
-            new java.io.File(cacheFile.getParent()).mkdirs();
-        } catch (Res.NotCacheableException e) {
-            Log.log(Freetype.class, "note: glyph not cacheable");
-        }
-        */
-        if (cacheFile != null && cacheFile.exists()) {
-            DataInputStream dis = new DataInputStream(new FileInputStream(cacheFile));
-            width = dis.readInt();
-            height = dis.readInt();
-            glyph.font.max_ascent = dis.readInt();
-            glyph.font.max_descent = dis.readInt();
-            glyph.baseline = dis.readInt();
-            glyph.advance = dis.readInt();
-            data = new byte[width * height];
-            if (width != 0 && height != 0) dis.readFully(data);
-            
-        } else try {
-            //System.out.println("cache miss!");
             if (loadedStream != glyph.font.res) loadFontByteStream(glyph.font.res);
             vm.setUserInfo(2, (int)glyph.c);
             vm.setUserInfo(3, (int)glyph.c);
@@ -84,29 +57,15 @@ public class Freetype {
             
             width = vm.getUserInfo(6);
             height = vm.getUserInfo(7);
-
+            
             data = new byte[width * height];
             int addr = vm.getUserInfo(5);
             vm.copyin(addr,data,width*height);
-            if (cacheFile != null) {
-                File tmp = new File(cacheFile.getCanonicalPath() + ".tmp");
-                DataOutputStream dis = new DataOutputStream(new FileOutputStream(tmp));
-                dis.writeInt(width);
-                dis.writeInt(height);
-                dis.writeInt(glyph.font.max_ascent);
-                dis.writeInt(glyph.font.max_descent);
-                dis.writeInt(glyph.baseline);
-                dis.writeInt(glyph.advance);
-                if (width != 0 && height != 0) dis.write(data, 0, data.length);
-                dis.close();
-                tmp.renameTo(cacheFile);
-            }
-
+            
+            if (width == 0 || height == 0) Log.log(Freetype.class, "warning glyph has zero width/height");
+            glyph.p = Platform.createAlphaOnlyPicture(data, width, height);
         } catch (Exception e) {
             Log.log(this, e);
         }
-
-        if (width == 0 || height == 0) Log.log(Freetype.class, "warning glyph has zero width/height");
-        glyph.p = Platform.createAlphaOnlyPicture(data, width, height);
     }
 }
index 2af26e8..3275bb9 100644 (file)
@@ -58,7 +58,7 @@ public class MSPack {
                 System.out.println("" + fileNames[i]);
                 vm.copyin(vm.memRead(addr+4),data[i],length);
             }
-        } catch(VM.EmulationException e) {
+        } catch(Exception e) {
             e.printStackTrace();
             throw new MSPackException("mspack.mips crashed");
         }