From 0b0673bbc7f06c5d5418d5ab7ad5961a464e2de0 Mon Sep 17 00:00:00 2001 From: megacz Date: Fri, 30 Jan 2004 07:41:30 +0000 Subject: [PATCH] 2003/11/16 08:28:10 darcs-hash:20040130074130-2ba56-e9919e297839a7213b832dd4750944a4334410c8.gz --- src/org/xwt/Picture.java | 31 +- src/org/xwt/Platform.java | 14 - src/org/xwt/Res.java | 13 - src/org/xwt/Surface.java | 30 +- src/org/xwt/Template.java | 3 +- src/org/xwt/XWT.java | 12 +- src/org/xwt/js/JSContext.java | 20 +- src/org/xwt/js/JSFunction.java | 511 +----------- src/org/xwt/js/JSTrap.java | 3 - src/org/xwt/mips/Compiler.java | 1190 +++++++++++++++++----------- src/org/xwt/mips/ELF.java | 119 ++- src/org/xwt/mips/Errno.java | 121 +++ src/org/xwt/mips/Interpreter.java | 151 ++-- src/org/xwt/mips/Registers.java | 41 + src/org/xwt/mips/{VM.java => Runtime.java} | 739 +++++++++-------- src/org/xwt/mips/Syscalls.java | 16 + src/org/xwt/mips/linker.ld | 27 +- src/org/xwt/translators/Freetype.java | 55 +- src/org/xwt/translators/MSPack.java | 2 +- 19 files changed, 1541 insertions(+), 1557 deletions(-) create mode 100644 src/org/xwt/mips/Errno.java create mode 100644 src/org/xwt/mips/Registers.java rename src/org/xwt/mips/{VM.java => Runtime.java} (51%) create mode 100644 src/org/xwt/mips/Syscalls.java diff --git a/src/org/xwt/Picture.java b/src/org/xwt/Picture.java index 3168390..13440f9 100644 --- a/src/org/xwt/Picture.java +++ b/src/org/xwt/Picture.java @@ -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; } } diff --git a/src/org/xwt/Platform.java b/src/org/xwt/Platform.java index 9c1d429..00b00d2 100644 --- a/src/org/xwt/Platform.java +++ b/src/org/xwt/Platform.java @@ -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); } diff --git a/src/org/xwt/Res.java b/src/org/xwt/Res.java index 53da6ff..e9a9465 100644 --- a/src/org/xwt/Res.java +++ b/src/org/xwt/Res.java @@ -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"; } diff --git a/src/org/xwt/Surface.java b/src/org/xwt/Surface.java index 36c82ee..3a083cc 100644 --- a/src/org/xwt/Surface.java +++ b/src/org/xwt/Surface.java @@ -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; iunpauseable 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 unpauseable 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> 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); } - } } diff --git a/src/org/xwt/js/JSTrap.java b/src/org/xwt/js/JSTrap.java index 3eefe14..56a076a 100644 --- a/src/org/xwt/js/JSTrap.java +++ b/src/org/xwt/js/JSTrap.java @@ -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; diff --git a/src/org/xwt/mips/Compiler.java b/src/org/xwt/mips/Compiler.java index 21fe7e5..ff6369e 100644 --- a/src/org/xwt/mips/Compiler.java +++ b/src/org/xwt/mips/Compiler.java @@ -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 "); - 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= (brk<> PAGE_SHIFT; - // FIXME: set memsize, serialize readPages/writePages - for(int j=0;j>> 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>> 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>> 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;inumbytes from the stream, emitting case blocks starting at vaddr ofs */ - 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= 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>> 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= 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(); } } diff --git a/src/org/xwt/mips/ELF.java b/src/org/xwt/mips/ELF.java index 91dd79d..12821f0 100644 --- a/src/org/xwt/mips/ELF.java +++ b/src/org/xwt/mips/ELF.java @@ -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= 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> 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 index 0000000..79603c4 --- /dev/null +++ b/src/org/xwt/mips/Registers.java @@ -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; +} diff --git a/src/org/xwt/mips/VM.java b/src/org/xwt/mips/Runtime.java similarity index 51% rename from src/org/xwt/mips/VM.java rename to src/org/xwt/mips/Runtime.java index b0bbf7b..89918a7 100644 --- a/src/org/xwt/mips/VM.java +++ b/src/org/xwt/mips/Runtime.java @@ -1,119 +1,171 @@ +// 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 allowEmptyPages is set */ + public Runtime(boolean allowEmptyPages) { + this.allowEmptyPages = allowEmptyPages; readPages = new int[TOTAL_PAGES][]; writePages = new int[TOTAL_PAGES][]; for(int i=0;isrc to addr initializing uninitialized pages if required. + Newly initalized pages will be marked read-only if ro is set */ + protected final void initPages(int[] src, int addr, boolean ro) { + for(int i=0;i>> 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 words of pages starting at addr to 0 */ + protected final void clearPages(int addr, int words) { + for(int i=0;i>> 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;jlength bytes from the processes memory space starting at + addr INTO a java byte array a */ + 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>>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 length bytes OUT OF the java array a into the processes memory + space at addr */ + 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 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 addr */ 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 addr */ + 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 page */ + private final void initPage(int page) { initPage(page,false); } + /** Created a new non-empty page at page number page. If ro 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, args, 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 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= 1024) throw new EmulationException("setUserInfo called with index >= 1024"); - memWrite(USER_INFO_ADDR+index*4,word); + /** Sets word number index in the _user_info table to word + _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 index + @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 fileName. 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 fd 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= 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. + incr is how much to increase the break by */ public int sbrk(int incr) { if(incr==0) return brk<= Integer.MAX_VALUE) return -EOPNOTSUPP; - } else { - if((flags & O_CREAT) == 0) return -ENOENT; - } - for(int i=0;i= 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. + syscall should be the contents of V0 and a, b, c, and d 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 index 0000000..26829df --- /dev/null +++ b/src/org/xwt/mips/Syscalls.java @@ -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; +} diff --git a/src/org/xwt/mips/linker.ld b/src/org/xwt/mips/linker.ld index 374cf25..5b8ab8b 100644 --- a/src/org/xwt/mips/linker.ld +++ b/src/org/xwt/mips/linker.ld @@ -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 : { diff --git a/src/org/xwt/translators/Freetype.java b/src/org/xwt/translators/Freetype.java index 0726fda..3079606 100644 --- a/src/org/xwt/translators/Freetype.java +++ b/src/org/xwt/translators/Freetype.java @@ -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); } } diff --git a/src/org/xwt/translators/MSPack.java b/src/org/xwt/translators/MSPack.java index 2af26e8..3275bb9 100644 --- a/src/org/xwt/translators/MSPack.java +++ b/src/org/xwt/translators/MSPack.java @@ -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"); } -- 1.7.10.4