2003/11/16 08:28:10
[org.ibex.core.git] / src / org / xwt / mips / Compiler.java
index 21fe7e5..ff6369e 100644 (file)
@@ -4,29 +4,58 @@ package org.xwt.mips;
 import java.util.*;
 import java.io.*;
 
-// FIXME: lb/sb/sh/lh need not be word aligned
-// FIXME: memory accesses aren't handling sign-extending properly
-// FIXME: probably have to implement nonaligned access
-// FIXME: implement malloc()
-// FIXME: implement an ELF parser based on RandomAccessFile
 // FEATURE: progress indicator
-// FEATURE: support n32 abi (passes more arguments in registers)
-// FEATURE: trap on arithmetic overflows
-// FEATURE: FPU
-// FEATURE: we always know the value of the pc register; we should emit it as a literal when it appears in computations
-// FEATURE: emit bytecode rather than .java code (for on-the-fly classloading without javac present in "real" JVMs)
+// FEATURE: emit bytecode rather than .java code (for on-the-fly classloading without javac present in "real" JVMs
 
-/** reads a fully linked MIPS ELF binary image on stdin; writes a .java file on stdout */
-public class Compiler {
+public class Compiler implements Registers {
 
-    static String runs = "";
-    static int last_emit = -1;
-    static DataInputStream dis;
-    public static void main(String[] s) throws IOException {
+    private static StringBuffer runs = new StringBuffer();
+    private static StringBuffer inits = new StringBuffer();
+    
+    private static PrintStream out = System.out;
+    
+    private static int indent;
+    private static String indents[] = new String[16];
+    static { String s=""; for(int i=0;i<indents.length;i++,s=s+"    ") indents[i] = s; }
+    private static final void p() { out.println(); }
+    private static final void p(String s) { out.println(indents[indent] + s); }
+    
+    // FEATURE: This should probably provide some detail about where the excpetion happened (address, line numbers, etc)
+    private static class CompilationException extends Exception { public CompilationException(String s) { super(s); } }
+    
+    // Set this to true to enable fast memory access 
+    // When this is enabled a Java RuntimeException will be thrown when a page fault occures. When it is disabled
+    // a FaultException will be throw which is easier to catch and deal with, however. as the name implies, this is slower
+    private static boolean fastMem = true;
+    
+    // This MUST be a power of two. If it is not horrible things will happen
+    // NOTE: This value can be much higher without breaking the classfile 
+    // specs (around 1024) but Hotstop seems to do much better with smaller
+    // methods. 
+    private static int MAX_INSN_PER_METHOD = 32;
+    
+    // Don't touch this
+    private static int MAX_BYTES_PER_METHOD = MAX_INSN_PER_METHOD*4;
+    private static int METHOD_MASK = ~(MAX_BYTES_PER_METHOD-1);
+    
+    // Store frequently used registers in local variables
+    // Unfortunately this doesn't seem to speed things up much
+    private static String[] freqRegs = { /*"r2", "r29", "r3", "r16", "r5", "r17", "r6", "r18", "r4", "r31", "r19"*/ };
+
+    // True to try to determine which case statement are needed and only include them
+    private static boolean pruneCases = true;
+    
+    // True to insert some code in the output to help diagnore compiler problems
+    private final static boolean debugCompiler = false;
+    
+    // True to print various statistics about the compilation
+    private final static boolean printStats = true;
+            
+    public static void main(String[] s) throws Exception {
 
         if (s.length != 2) {
             System.err.println("usage: java " + Compiler.class.getName() + " <classname> <binary.mips>");
-            System.exit(-1);
+            System.exit(1);
         }
 
         String packageName = null;
@@ -35,160 +64,360 @@ public class Compiler {
             packageName = s[0].substring(0, s[0].lastIndexOf('.'));
             className = s[0].substring(s[0].lastIndexOf('.') + 1);
         }
-
-        System.out.println(prefix + "// This file was generated by MipsToJava");
-        if (packageName != null) System.out.println(prefix + "package " + packageName + ";");
-        System.out.println(prefix + "public class " + className + " extends org.xwt.mips.VM {");
-        System.out.println(prefix + "");
-        System.out.println(prefix + "    public " + className + "() { }");
-        System.out.println(prefix + "");
-        System.out.println(prefix + "    // program counter");
-        System.out.println(prefix + "    int pc = 0;");
-        System.out.println(prefix + "");
-        System.out.println(prefix + "    // temporary");
-        System.out.println(prefix + "    int tmp = 0;");
-        System.out.println(prefix + "");
-        System.out.println(prefix + "    // MIPS multiply/divide subsystem; 64-bit result");
-        System.out.println(prefix + "    long hilo = 0;");
-        System.out.println(prefix + "");
-        System.out.println(prefix + "    // General Purpose registers");
-        System.out.println(prefix + "    final int r0 = 0;");
-        System.out.println(prefix + "    int r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0,");
-        System.out.println(prefix + "        r8 = 0, r9 = 0, r10 = 0, r11 = 0, r12 = 0, r13 = 0, r14 = 0, r15 = 0,");
-        System.out.println(prefix + "        r16 = 0, r17 = 0, r18 = 0, r19 = 0, r20 = 0, r21 = 0, r22 = 0, r23 = 0,");
-        System.out.println(prefix + "        r24 = 0, r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0, r30 = 0, r31 = 0;");
-        System.out.println(prefix + "");
-
-        ELF elf = new ELF(new RandomAccessFile(args[1], "r"));
+        
+        ELF elf = new ELF(s[1]);
         if(elf.header.type != ELF.ELFHeader.ET_EXEC) throw new IOException("Binary is not an executable");
         if(elf.header.machine != ELF.ELFHeader.EM_MIPS) throw new IOException("Binary is not for the MIPS I Architecture");
 
-        ELF.PHeader[] pheaders = elf.pheaders;
-
-        int[][] readPages = new int[TOTAL_PAGES][];
-        int[][] writePages = new int[TOTAL_PAGES][];
-        for(int i=0; i<STACK_PAGES; i++)
-            readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage;
-
-        // FIXME: what is this?
-        int brk = 0;
-        for(int i=0;i<pheaders.length;i++) {
-            ELF.PHeader ph = pheaders[i];
-            if(ph.type != ELF.PHeader.PT_LOAD) continue;
-            int memsize = ph.memsz;
-            int filesize = ph.filesz;
-            if(memsize == 0) continue;
-            if(memsize < 0) throw new IOException("pheader size too large");
-            int addr = ph.vaddr;
-            if(addr == 0x0) throw new IOException("pheader vaddr == 0x0");
-            if(addr+memsize >= (brk<<PAGE_SHIFT)) brk = (addr+memsize+PAGE_SIZE-1) >> PAGE_SHIFT;
-            // FIXME: set memsize, serialize readPages/writePages
-            for(int j=0;j<memsize+PAGE_SIZE-1;j+=PAGE_SIZE) {
-                int page = (j+addr) >>> PAGE_SHIFT;
-                if(readPages[page] == null) readPages[page] = new int[PAGE_WORDS];
-                if(ph.writable()) writePages[page] = readPages[page];
+        p("// This file was generated by MipsToJava");
+        if (packageName != null) p("package " + packageName + ";");
+        p("public class " + className + " extends Runtime {");
+        p("");
+        p("    // program counter");
+        p("    private int pc = 0;");
+        if(debugCompiler)
+            p("    private int lastPC = 0;");
+        p("");
+        p("    // General Purpose registers");
+        p("    private final static int r0 = 0;");
+        p("    int r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0,");
+        p("        r8 = 0, r9 = 0, r10 = 0, r11 = 0, r12 = 0, r13 = 0, r14 = 0, r15 = 0,");
+        p("        r16 = 0, r17 = 0, r18 = 0, r19 = 0, r20 = 0, r21 = 0, r22 = 0, r23 = 0,");
+        p("        r24 = 0, r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0, r30 = 0, r31 = 0,");
+        p("        hi = 0, lo = 0;");
+        p("    // FP registers");
+        p("    private int f0 = 0, f1 = 0, f2 = 0, f3 = 0, f4 = 0, f5 = 0, f6 = 0, f7 = 0,");
+        p("        f8 = 0, f9 = 0, f10 = 0, f11 = 0, f12 = 0, f13 = 0, f14 = 0, f15 = 0,");
+        p("        f16 = 0, f17 = 0, f18 = 0, f19 = 0, f20 = 0, f21 = 0, f22 = 0, f23 = 0,");
+        p("        f24 = 0, f25 = 0, f26 = 0, f27 = 0, f28 = 0, f29 = 0, f30 = 0, f31 = 0;");
+        p("    // FP Control Register");        
+        p("    private int fcsr = 0;");
+        p("");
+        indent++;
+        // These should all be inlind by javac
+        p("private final void setFC(boolean b) { fcsr = (fcsr&~0x800000) | (b ? 0x800000 : 0x000000); }");
+        p("private final int roundingMode() { return fcsr & 3; /* bits 0-1 */ }");
+        indent--;
+        
+        Set jumpableAddresses = null;
+        if(pruneCases) {
+            // Find all possible branches
+            jumpableAddresses = new HashSet();
+            
+            jumpableAddresses.add(new Integer(elf.header.entry));
+            
+            ELF.SHeader text = elf.sectionWithName(".text");
+            if(text == null) throw new Error("No .text segment");
+            findBranchesInText(text.addr,new DataInputStream(text.getInputStream()),text.size,jumpableAddresses);
+            
+            findBranchesInSymtab(elf.getSymtab(),jumpableAddresses);            
+            
+            for(int i=0;i<elf.sheaders.length;i++) {
+                ELF.SHeader sheader = elf.sheaders[i];
+                String name = sheader.name;
+                // if this section doesn't get loaded into our address space don't worry about it
+                if(sheader.addr == 0x0) continue;
+                if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
+                    findBranchesInData(new DataInputStream(sheader.getInputStream()),sheader.size,jumpableAddresses,text.addr,text.addr+text.size);
             }
-            if(filesize != 0) {
-                filesize = filesize & ~3;
-                DataInputStream dis = new DataInputStream(ph.getInputStream());
-                do {
-                    if (ph.executable()) emit(addr, 1, dis);
-                    readPages[addr >>> PAGE_SHIFT][(addr >>> 2)&(PAGE_WORDS-1)] = dis.readInt();
-                    addr+=4;
-                    filesize-=4;
-                } while(filesize > 0);
-                dis.close();
+        }
+        
+        // Generate main body functions
+        int highestAddr = 0;
+        indent=1;
+        for(int i=0;i<elf.sheaders.length;i++) {
+            ELF.SHeader sheader = elf.sheaders[i];
+            String name = sheader.name;
+            // if this section doesn't get loaded into our address space don't worry about it
+            if(sheader.addr == 0x0) continue;
+            
+            highestAddr = Math.max(highestAddr, sheader.addr + sheader.size);
+            
+            if(name.equals(".text")) {
+                emitText(sheader.addr, new DataInputStream(sheader.getInputStream()),sheader.size,jumpableAddresses);
+                endMethod(nextEmitTextAddr);
+            } else if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors")) {
+                emitData(sheader.addr, new DataInputStream(sheader.getInputStream()), sheader.size,name.equals(".rodata")); 
+            } else if(name.equals(".bss") || name.equals(".sbss")) {
+                if(sheader.entsize != 0) throw new CompilationException("bss segment has data!");
+                emitBSS(sheader.addr,sheader.size);
+            } else {
+                throw new CompilationException("Unknown segment: " + name);
             }
+        }        
+        indent = 0;
+        
+        p("    public " + className + "() throws FaultException {");
+        if(fastMem) {
+            p("        super(false); // don't allow empty pages");
+            p("        if(PAGE_SIZE != " + toHex(Runtime.PAGE_SIZE) + ") throw new Error(\"Runtime.PAGE_SIZE mismatch\");");
+        } else {
+            p("        super(true); // allow empty pages");
         }
+        p("        // init data");
+        p("        entryPoint = " + toHex(elf.header.entry) + ";");
+        p(inits.toString());
+        p("        brk = (" + toHex(highestAddr) + "+PAGE_SIZE-1) >>> PAGE_SHIFT;");
+        p("        state = INITIALIZED;");
+        p("    }");
+        p("");
+        p();
+        p("    public static void main(String[] javaArgs) throws Exception {");
+        p("        String[] args = new String[javaArgs.length+1];");
+        p("        System.arraycopy(javaArgs,0,args,1,javaArgs.length);");
+        p("        args[0] = \"" + className + "\";");
+        p("        " + className + " me = new " + className + "();");
+        p("        // User data");
+        p("        int addr = me.sbrk(PAGE_SIZE);");
+        p("        for(int i=0;i<10;i++) {");
+        p("            String s = \"User Info item: \" + (i+1) + \"\\0\";");
+        p("            byte[] b = s.getBytes(\"US-ASCII\");");
+        p("            me.copyout(b,addr,b.length);");
+        p("            me.setUserInfo(i,addr);");
+        p("            addr += b.length;");
+        p("        }");
+        p("        // End user data");
+        p("        int status = me.run(args);");
+        p("        System.err.println(\"Exit status: \" + status);");
+        p("        System.exit(status);");
+        p("        }");
+        p();
+        p("    protected void _start(int pc) {");
+        p("        // set the stack pointer");
+                p("        r26 = STUFF_BASE;");
+                p("        r27 = PAGE_SIZE;");
+        p("        r29 = INITIAL_SP;");
+        p("        // set the \"return address\" from _start to point at the \"magic exit address\" (0xdeadbeef)");
+        p("        r31 = 0xdeadbeef;");
+        p("        this.pc = pc;");
+        p("    }");
 
-        // cap off the last method
-        emit(-1, -1, dis);
-
-        System.out.println(prefix + "    protected int state = INITIALIZED;");
-        System.out.println(prefix + "    protected int brk = " + brk + ";");
+        p();
+        p("    protected void _execute() throws ExecutionException { trampoline(); }");
+        p();
+        p("    private final void trampoline() throws ExecutionException {");
+        p("        boolean finished = false;");
+        p("        while(!finished) {");
+        p("            switch(this.pc & " + toHex(~(MAX_BYTES_PER_METHOD-1)) + ") {");
+        p(runs.toString());
+        p("                default: throw new Error(\"invalid address 0x\" + Long.toString(this.pc&0xffffffffL,16));");
+        p("            }");
+        p("        }");
+        p("    }");
+        p("}");
+    }
 
-        System.out.println();
-        System.out.println(prefix + "    public static void main(String[] s) { new " + className + "()._start(entryPoint); }");
-        System.out.println();
-        System.out.println(prefix + "    protected int entryPoint = " + elf.header.entry + ";");
-        System.out.println();
-        System.out.println(prefix + "    protected void _start(int pc) {");
-        System.out.println();
-        System.out.println(prefix + "        // set the stack pointer");
-        System.out.println(prefix + "        r29 = INITIAL_SP;");
-        System.out.println();
-        System.out.println(prefix + "        // set the \"return address\" from _start to point at the \"magic exit address\" (0xdeadbeef)");
-        System.out.println(prefix + "        r31 = 0xdeadbeef;");
-        System.out.println();
-        System.out.println(prefix + "        // read in the .data segment");
-        System.out.println(prefix + "        //initData();");
-        System.out.println();
-        System.out.println(prefix + "        trampoline(pc);");
-        System.out.println();
-        System.out.println(prefix + "    }");
+    private static int startOfMethod = 0;
+    private static int endOfMethod = 0;
+    
+    private static void startMethod(int addr) {
+        addr &= ~(MAX_BYTES_PER_METHOD-1);
+        endOfMethod = addr + MAX_BYTES_PER_METHOD;
+        String methodName = "run_" + Long.toString(addr & 0xffffffffL, 16);
+        runs.append(indents[4] + "case " + toHex(addr) + ": finished = !" + methodName + "(); break;\n");
+        p("private final boolean " + methodName + "() throws ExecutionException { /"+"* " + toHex(addr) + " - " + toHex(endOfMethod) + " *" + "/");
+        indent++;
+        p("int addr, tmp;");
+        for(int i=0;i<freqRegs.length;i++)
+            p("int " + freqRegs[i] + " = this." + freqRegs[i] + ";");
+        p("for(;;) {");
+        indent++;
+        p("switch(pc) {");
+        indent++;
+        startOfMethod = addr;
+        
+    }
+    private static void endMethod() { endMethod(endOfMethod); }
+    private static void endMethod(int lastAddr) {
+        if(startOfMethod == 0) return;
+        // This isn't strictly necessary; its just here to work around unreachable code errors
+        p("case " + toHex(lastAddr) + ":");
+        indent++;
+        p("pc=" + toHex(lastAddr) + ";");
+        leaveMethod();
+        indent--;
+        if(debugCompiler)
+            p("default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16)  + \" (got here from 0x\" + Long.toString(lastPC&0xffffffffL,16)+\")\");");
+        else
+            p("default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
+        indent--;
+        p("}"); // end switch
+        p("/* NOT REACHED */");
+        indent--;
+        p("}"); // end for
+        indent--;
+        p("}"); // end method
+        endOfMethod = 0;
+    }
 
-        System.out.println();
-        System.out.println(prefix + "    public void execute() throws EmulationException {");
-        System.out.println(prefix + "        if(state == PAUSED) state = RUNNING;");
-        System.out.println(prefix + "        if(state != RUNNING)");
-        System.out.println(prefix + "             throw new IllegalStateException(\"execute() called in inappropriate state\");");
-        System.out.println(prefix + "        trampoline(this.pc);");
-        System.out.println(prefix + "    }");
-        System.out.println();
-        System.out.println(prefix + "    public void trampoline(int pc) {");
-        System.out.println(prefix + "        this.pc = pc;");
-        System.out.println(prefix + "        while(true) {");
-        System.out.println(prefix + "            switch(this.pc & 0xffffff00) {");
-        System.out.println(prefix + "                case 0xdeadbe00: System.out.println(\"exiting with return value \" + r2); System.exit(r2); continue;");
-        System.out.print(runs);
-        System.out.println(prefix + "                default: throw new Error(\"invalid address 0x\" + Long.toString(this.pc&0xffffffffL,16));");
-        System.out.println(prefix + "            }");
-        System.out.println(prefix + "        }");
-        System.out.println(prefix + "    }");
-        System.out.println(prefix + "}");
+    private static void branch(int pc, int target) {
+        if(debugCompiler)
+            p("lastPC = " + toHex(pc) + ";");
+        p("pc=" + toHex(target) + ";");
+        if((pc&METHOD_MASK) == (target&METHOD_MASK))
+            p("continue;");
+        else
+            leaveMethod();
+    }
+    
+    private static void leaveMethod() { leaveMethod(true); }
+    private static void leaveMethod(boolean cont) {
+        for(int i=0;i<freqRegs.length;i++)
+            p("this." + freqRegs[i] + " = " + freqRegs[i] + ";");
+        p("return " + (cont?"true":"false") + ";");
     }
 
-    static int _instruction;
-    static boolean readnext = true;
+    private static int nextEmitTextAddr = -1;
+    private static void emitText(int addr, DataInputStream dis, int size, Set jumpableAddresses) throws CompilationException,IOException {
+        if(addr < nextEmitTextAddr) throw new CompilationException("Out of order sections");
+        if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
+        int count = size/4;
+        int nextInsn = dis.readInt();
+        if(nextInsn == -1) throw new Error("Actually read -1 at " + toHex(addr));
+        int insn;
 
-    /** reads <tt>numbytes</tt> from the stream, emitting <tt>case</tt> blocks starting at vaddr <tt>ofs</tt> */
-    static void emit(int vaddr, int numbytes, DataInputStream dis) throws IOException {
-        if ((vaddr == -1 && numbytes == -1) || (last_emit != -1 && ((last_emit & 0xffffff00) != (vaddr & 0xffffff00)))) {
-            System.out.println(prefix + "                case 0x" + Long.toString((last_emit & 0xffffffffL) + 0x1000, 16) + ": return;");
-            System.out.println(prefix + "                default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
-            System.out.println(prefix + "        }");
-            System.out.println(prefix + "    }");
-            if (vaddr == -1 && numbytes == -1) return;
+        for(int i=0;i<count;i++,addr+=4) {
+            insn = nextInsn;
+            nextInsn = (i == count-1) ? -1 : dis.readInt();
+            if(addr >= endOfMethod) { endMethod(); startMethod(addr); }
+            if(jumpableAddresses==null || addr == startOfMethod || jumpableAddresses.contains(new Integer(addr))) {
+                p("case " + toHex(addr) + ":");
+                unreachable = false;
+            } else if(unreachable) {
+                continue;
+            } else if(debugCompiler) {
+                p("/" + "* pc = " + toHex(addr) + "*" + "/");
+            }
+            indent++;
+            emitInstruction(addr,insn,nextInsn);
+            indent--;
         }
-        if (last_emit == -1 || ((last_emit & 0xffffff00) != (vaddr & 0xffffff00))) {
-            System.out.println("");
-            System.out.println(prefix + "    private void run_" + Long.toString(vaddr & 0xffffff00L,16) + "() {");
-            runs += "                case 0x" + Long.toString(vaddr & 0xffffff00L,16) +
-                ": run_" + Long.toString(vaddr & 0xffffff00L,16) + "(); continue;\n";
-            System.out.println(prefix + "        switch(pc) {");
+        nextEmitTextAddr = addr;
+        dis.close();
+    }
+    
+    private static int initDataCount = 0;
+    private static void emitData(int addr, DataInputStream dis, int size, boolean readOnly) throws CompilationException,IOException {
+        if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
+        int count = size/4;
+        String varname =  "_data" + (++initDataCount);
+        p("private final static int[] " + varname +  " = {");
+        indent++;
+        for(int i=0;i<count;) {
+            StringBuffer sb = new StringBuffer();
+            for(int j=0;j<8 && i<count;j++,i++) {
+                sb.append(toHex8(dis.readInt()));
+                if(i!=count-1) sb.append(",");
+            }
+            p(sb.toString());
         }
-        int ofs = vaddr;
-        try {
-            OUTER: for(ofs = vaddr; ofs < vaddr + numbytes; ofs+=4) {
-                if (readnext) _instruction = dis.readInt();
-                readnext = true;
-
-                String istring = Long.toString(_instruction & 0xffffffffL, 16);
-                while(istring.length() < 8) istring = "0" + istring;
-                String ostring = Long.toString(ofs & 0xffffffffL, 16);
-                while(ostring.length() < 8) ostring = "0" + ostring;
-                System.out.print(prefix + "                /* " + istring + " */   case 0x" + ostring + ": System.out.println(\"pc=0x\" + Long.toString(pc&0xffffffffL,16));");
-
-                emit_instruction(ofs, _instruction);
+        indent--;
+        p("};");
+        inits.append(indents[2] + "initPages(" + varname + "," + toHex(addr) + "," + (readOnly?"true":"false") + ");\n");
+        dis.close();
+    }
+    
+    private static int initBSSCount = 0;
+    private static void emitBSS(int addr, int size) throws CompilationException,IOException {
+        if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
+        int count = size/4;
+        inits.append(indents[2] + "clearPages(" + toHex(addr) + "," + toHex(count) + ");\n");
+    }
+    
+    private static void findBranchesInSymtab(ELF.Symtab symtab, Set jumps) {
+        ELF.Symbol[] symbols = symtab.symbols;
+        int n=0;
+        for(int i=0;i<symbols.length;i++) {
+            ELF.Symbol s = symbols[i];
+            if(s.type == ELF.Symbol.STT_FUNC) {
+                //System.err.println("Adding symbol: " + s.name + " at " + toHex(s.addr));
+                if(jumps.add(new Integer(s.addr))) n++;
             }
-        } catch (EOFException e) {
-            emit(ofs, "                // warning, reached EOF before section end");
-        } finally {
-            last_emit = ofs;
-        }  
+        }
+        if(printStats) System.err.println("Found " + n + " additional possible branch targets in Symtab");
     }
-
-    private static void emit_instruction(int pc, int insn) throws IOException {
+    
+    private static void findBranchesInText(int addr, DataInputStream dis, int size, Set jumps) throws IOException {
+        int count = size/4;
+        int pc = addr;
+        int n=0;
+        
+        for(int i=0;i<count;i++,pc+=4) {
+            int insn = dis.readInt();
+            int op = (insn >>> 26) & 0xff; 
+            int rs = (insn >>> 21) & 0x1f;
+            int rt = (insn >>> 16) & 0x1f;
+            int signedImmediate = (insn << 16) >> 16;
+            int branchTarget = signedImmediate;
+            int jumpTarget = (insn & 0x03ffffff);
+            int subcode = insn & 0x3f;
+            
+            switch(op) {
+                case 0:
+                    switch(subcode) {
+                        case 9: // JALR
+                            if(jumps.add(new Integer(pc+8))) n++; // return address
+                            break;
+                        case 12: // SYSCALL
+                            if(jumps.add(new Integer(pc+4))) n++; 
+                            break;
+                    }
+                    break;
+                case 1:
+                    switch(rt) {
+                        case 16: // BLTZAL
+                        case 17: // BGTZAL
+                            if(jumps.add(new Integer(pc+8))) n++; // return address
+                            // fall through
+                        case 0: // BLTZ
+                        case 1: // BGEZ
+                            if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
+                            break;
+                    }
+                    break;
+                case 3: // JAL
+                    if(jumps.add(new Integer(pc+8))) n++; // return address
+                    // fall through
+                case 2: // J
+                    if(jumps.add(new Integer((pc&0xf0000000)|(jumpTarget << 2)))) n++;
+                    break;
+                case 4: // BEQ
+                case 5: // BNE
+                case 6: // BLEZ
+                case 7: // BGTZ
+                    if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
+                    break;
+                case 17: // FPU Instructions
+                    switch(rs) {
+                        case 8: // BC1F, BC1T
+                            if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
+                            break;
+                    }
+                    break;
+            }
+        }
+        dis.close();
+        if(printStats) System.err.println("Found " + n + " additional possible branch targets in Text segment");
+    }
+    
+    private static void findBranchesInData(DataInputStream dis, int size, Set jumps, int textStart, int textEnd) throws IOException {
+        int count = size/4;
+        int n=0;
+        for(int i=0;i<count;i++) {
+            int word = dis.readInt();
+            if((word&3)==0 && word >= textStart && word < textEnd) {
+                if(jumps.add(new Integer(word))) n++;
+            }
+        }
+        dis.close();
+        if(n>0 && printStats) System.err.println("Found " + n + " additional possible branch targets in Data segment");
+    }
+    
+    private static boolean unreachable = false;
+    
+    private static void emitInstruction(int pc, int insn, int nextInsn) throws IOException,CompilationException {
+        if(insn == -1) throw new Error("insn is -1");
+        
         int op = (insn >>> 26) & 0xff;                 // bits 26-31
         int rs = (insn >>> 21) & 0x1f;                 // bits 21-25
         int rt = (insn >>> 16) & 0x1f;                 // bits 16-20 
@@ -206,99 +435,125 @@ public class Compiler {
 
         int tmp, addr; // temporaries
         
+        //if(pc%64==0) p("System.err.println(\"Executing: " + toHex(pc) + "\");");
+        //p("/" + "*" + (pc == -1 ? "Delay Slot"  : toHex(pc)) + " *" + "/ ");
+        if(pc==-1) p("/" + "* Next insn is delay slot *" + "/ ");
+        
         switch(op) {
             case 0: {
                 switch(subcode) {
                     case 0: // SLL
-                        if(insn == 0) break;
-                        emit(pc, "r"+rd+" = r"+rt+" << "+shamt+";");
+                        if(insn == 0) 
+                            p("/* NOOP */"); 
+                        else
+                            p( "r"+rd+" = r"+rt+" << "+shamt+";");
                         break;
                     case 2: // SRL
-                        emit(pc, "r"+rd+" = r"+rt+" >>> "+shamt+";");
+                        p( "r"+rd+" = r"+rt+" >>> "+shamt+";");
                         break;
                     case 3: // SRA
-                        emit(pc, "r"+rd+" = r"+rt+" >> "+shamt+";");
+                        p( "r"+rd+" = r"+rt+" >> "+shamt+";");
                         break;
-                        // FIXME: Do we need % 32 on the r"+rs+" ?
                     case 4: // SLLV
-                        emit(pc, "r"+rd+" = r"+rt+" << r"+rs+";");
+                        p( "r"+rd+" = r"+rt+" << (r"+rs+"&0x1f);");
                         break;
                     case 6: // SRLV
-                        emit(pc, "r"+rd+" = r"+rt+" >>> r"+rs+";");
+                        p( "r"+rd+" = r"+rt+" >>> (r"+rs+"&0x1f);");
                         break;
                     case 7: // SRAV
-                        emit(pc, "r"+rd+" = r"+rt+" >> r"+rs+";");
+                        p( "r"+rd+" = r"+rt+" >> (r"+rs+"&0x1f);");
                         break;
                     case 8: // JR
-                        emit(pc, "{ int tmp = r"+rs+"; pc += 4; nextPC = tmp; }");
-                        continue OUTER;
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        emitInstruction(-1,nextInsn,-1);
+                        if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
+                        p("pc=r" + rs + ";");
+                        leaveMethod();
+                        unreachable = true;
+                        break;
                     case 9: // JALR
-                        emit(pc, "{ int tmp = r"+rs+"; pc += 4; r"+rd+" = pc+4; nextPC = tmp; }");
-                        continue OUTER;
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        emitInstruction(-1,nextInsn,-1);
+                        if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
+                        p("pc=r" + rs + ";");
+                        p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
+                        leaveMethod();
+                        unreachable = true;
+                        break;
                     case 12: // SYSCALL
-                        emit(pc, "r"+V0+" = syscall(r"+V0+",r"+A0+",r"+A1+",r"+A2+",r"+A3+"); " +
-                             "if (state != RUNNING) { this.nextPC = nextPC; return; }");
+                        p( "r"+V0+" = syscall(r"+V0+",r"+A0+",r"+A1+",r"+A2+",r"+A3+");");
+                        p("if (state != RUNNING) {");
+                            indent++;
+                            p("pc = " + toHex(pc+4) + ";");
+                            leaveMethod(false);
+                            indent--;
+                        p("}");
                         break;
                     case 13: // BREAK
-                        emit(pc, "throw new EmulationException(\"Break\")");
+                        p( "throw new ExecutionException(\"Break\");");
                         break;
                     case 16: // MFHI
-                        emit(pc, "r"+rd+" = hi;");
+                        p( "r"+rd+" = hi;");
                         break;
                     case 17: // MTHI
-                        emit(pc, "hi = r"+rs+";");
+                        p( "hi = r"+rs+";");
                         break;
                     case 18: // MFLO
-                        emit(pc, "r"+rd+" = lo;");
+                        p( "r"+rd+" = lo;");
                         break;
                     case 19: // MTLO
-                        emit(pc, "lo = r"+rs+";");
+                        p( "lo = r"+rs+";");
                         break;
                     case 24: // MULT
-                        emit(pc, "long hilo = (long)(r"+rs+") * ((long)r"+rt+"); " +
+                        p( "{ long hilo = (long)(r"+rs+") * ((long)r"+rt+"); " +
                              "hi = (int) (hilo >>> 32); " +
-                             "lo = (int) hilo;");
+                             "lo = (int) hilo; }");
                         break;
                     case 25: // MULTU
-                        emit(pc, "long hilo = (r"+rs+" & 0xffffffffL) * (r"+rt+" & 0xffffffffL); " +
+                        p( "{ long hilo = (r"+rs+" & 0xffffffffL) * (r"+rt+" & 0xffffffffL); " +
                              "hi = (int) (hilo >>> 32); " +
-                             "lo = (int) hilo;");
+                             "lo = (int) hilo; } ");
+                        break;
                     case 26: // DIV
-                        emit(pc, "hi = r"+rs+"%r"+rt+"; lo = r"+rs+"/r"+rt+";");
+                        p( "hi = r"+rs+"%r"+rt+"; lo = r"+rs+"/r"+rt+";");
                         break;
                     case 27: // DIVU
-                        emit(pc, "hi = (int)((r"+rs+" & 0xffffffffL) % (r"+rt+" & 0xffffffffL)); " +
+                        p( "hi = (int)((r"+rs+" & 0xffffffffL) % (r"+rt+" & 0xffffffffL)); " +
                              "lo = (int)((r"+rs+" & 0xffffffffL) / (r"+rt+" & 0xffffffffL));");
                         break;
                     case 32: // ADD
-                        emit(pc, "r"+rd+" = r"+rs+" + r"+rt+";"); // FIXME: Trap on overflow
-                        break;
+                        throw new CompilationException("ADD (add with oveflow trap) not suported");
+                        /*This must trap on overflow
+                        p( "r"+rd+" = r"+rs+" + r"+rt+";");
+                        break;*/
                     case 33: // ADDU
-                        emit(pc, "r"+rd+" = r"+rs+" + r"+rt+";");
+                        p( "r"+rd+" = r"+rs+" + r"+rt+";");
                         break;
                     case 34: // SUB
-                        emit(pc, "r"+rd+" = r"+rs+" - r"+rt+";");  // FIXME: Trap on overflow
-                        break;
+                        throw new CompilationException("SUB (add with oveflow trap) not suported");
+                        /*This must trap on overflow
+                        p( "r"+rd+" = r"+rs+" - r"+rt+";");
+                        break;*/
                     case 35: // SUBU
-                        emit(pc, "r"+rd+" = r"+rs+" - r"+rt+";");
+                        p( "r"+rd+" = r"+rs+" - r"+rt+";");
                         break;
                     case 36: // AND
-                        emit(pc, "r"+rd+" = r"+rs+" & r"+rt+";");
+                        p( "r"+rd+" = r"+rs+" & r"+rt+";");
                         break;
                     case 37: // OR
-                        emit(pc, "r"+rd+" = r"+rs+" | r"+rt+";");
+                        p( "r"+rd+" = r"+rs+" | r"+rt+";");
                         break;
                     case 38: // XOR
-                        emit(pc, "r"+rd+" = r"+rs+" ^ r"+rt+";");
+                        p( "r"+rd+" = r"+rs+" ^ r"+rt+";");
                         break;
                     case 39: // NOR
-                        emit(pc, "r"+rd+" = ~(r"+rs+" | r"+rt+");");
+                        p( "r"+rd+" = ~(r"+rs+" | r"+rt+");");
                         break;
                     case 42: // SLT
-                        emit(pc, "r"+rd+" = r"+rs+" < r"+rt+" ? 1 : 0;");
+                        p( "r"+rd+" = r"+rs+" < r"+rt+" ? 1 : 0;");
                         break;
                     case 43: // SLTU
-                        emit(pc, "r"+rd+" = ((r"+rs+" & 0xffffffffL) < (r"+rt+" & 0xffffffffL)) ? 1 : 0;");
+                        p( "r"+rd+" = ((r"+rs+" & 0xffffffffL) < (r"+rt+" & 0xffffffffL)) ? 1 : 0;");
                         break;
                     default:
                         throw new RuntimeException("Illegal instruction 0/" + subcode);
@@ -308,411 +563,460 @@ public class Compiler {
             case 1: {
                 switch(rt) {
                     case 0: // BLTZ
-                        emit(pc, "if (r"+rs+"<0) { pc += 4; int tmp = pc + "+
-                             branchTarget+"*4; nextPC = tmp; }");
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        p("if(r" + rs + " < 0) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
                         break;
                     case 1: // BGEZ
-                        emit(pc, "if (r"+rs+">=0) { pc += 4; int tmp = pc + "+
-                             branchTarget+"*4; nextPC = tmp; }");
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        p("if(r" + rs + " >= 0) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
                         break;
                     case 16: // BLTZAL
-                        emit(pc, "if(r"+rs+" < 0) { pc += 4; r"+RA+" = pc+4; int tmp = pc + "+
-                             branchTarget+"*4; nextPC = tmp; }");
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        p("if(r" + rs + " < 0) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
                         break;
                     case 17: // BGEZAL
-                        emit(pc, "if(r"+rs+" >= 0) { pc += 4; r"+RA+" = pc+4; int tmp = pc + "+
-                             branchTarget+"*4; nextPC = tmp; }");
+                        if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                        p("if(r" + rs + " >= 0) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
                         break;
                     default:
-                        throw new RuntimeException("Illegal Instruction");
+                        throw new RuntimeException("Illegal Instruction 1/" + rt);
                 }
                 break;
             }
             case 2: { // J
-                emit(pc, "int tmp = (pc&0xf0000000) | ("+jumpTarget+" << 2); pc+=4; nextPC = tmp;");
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                emitInstruction(-1,nextInsn,-1);
+                branch(pc,(pc&0xf0000000)|(jumpTarget << 2));
+                unreachable = true;
                 break;
             }
             case 3: { // JAL
-                emit(pc, "int tmp = (pc&0xf0000000) | ("+jumpTarget+" << 2); pc+=4; r"+RA+
-                     " = pc+4; nextPC = tmp;");
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                emitInstruction(-1,nextInsn,-1);
+                p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
+                branch(pc, (pc&0xf0000000)|(jumpTarget << 2));
+                unreachable = true;
                 break;
             }
             case 4: // BEQ
-                emit(pc, "if(r"+rs+" == r"+rt+") { pc += 4; int tmp = pc + "+
-                     branchTarget+"*4; nextPC = tmp; }");
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                p("// BEQ");
+                p("if(r" + rs + " == r" + rt + ") {");
+                    indent++;
+                    emitInstruction(-1,nextInsn,-1);
+                    branch(pc,pc+branchTarget*4+4);
+                    indent--;
+                p("}");
                 break;
-            case 5: // BNE                
-                emit(pc, "if(r"+rs+" != r"+rt+") { pc += 4; int tmp = pc + "+
-                     branchTarget+"*4; nextPC = tmp; }");
+            case 5: // BNE       
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                p("if(r" + rs + " != r" + rt + ") {");
+                    indent++;
+                    emitInstruction(-1,nextInsn,-1);
+                    branch(pc,pc+branchTarget*4+4);
+                    indent--;
+                p("}");
                 break;
             case 6: //BLEZ
-                emit(pc, "if(r"+rs+" <= 0) { pc += 4; int tmp = pc + "+
-                     branchTarget+"*4; nextPC = tmp; ;");
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                p("if(r" + rs + " <= 0) {");
+                    indent++;
+                    emitInstruction(-1,nextInsn,-1);
+                    branch(pc,pc+branchTarget*4+4);
+                    indent--;
+                p("}");
                 break;
             case 7: //BGTZ
-                emit(pc, "if(r"+rs+" > 0) { pc += 4; int tmp = pc + "+branchTarget+"*4; nextPC = tmp; }");
+                if(pc == -1) throw new Error("pc modifying insn in delay slot");
+                p("if(r" + rs + " > 0) {");
+                    indent++;
+                    emitInstruction(-1,nextInsn,-1);
+                    branch(pc,pc+branchTarget*4+4);
+                    indent--;
+                p("}");
                 break;
             case 8: // ADDI
-                emit(pc, "r"+rt+" = r"+rs+" + "+signedImmediate +";");
+                p( "r"+rt+" = r"+rs+" + "+signedImmediate +";");
                 break;
             case 9: // ADDIU
-                emit(pc, "r"+rt+" = r"+rs+" + "+signedImmediate+";");
+                p( "r"+rt+" = r"+rs+" + "+signedImmediate+";");
                 break;
             case 10: // SLTI
-                emit(pc, "r"+rt+" = r"+rs+" < "+signedImmediate+" ? 1 : 0;");
+                p( "r"+rt+" = r"+rs+" < "+signedImmediate+" ? 1 : 0;");
                 break;
             case 11: // SLTIU
-                emit(pc, "r"+rt+" = (r"+rs+"&0xffffffffL) < ("+unsignedImmediate+"&0xffffffffL) ? 1 : 0;");
+                p( "r"+rt+" = (r"+rs+"&0xffffffffL) < ("+unsignedImmediate+"&0xffffffffL) ? 1 : 0;");
                 break;
             case 12: // ANDI
-                emit(pc, "r"+rt+" = r"+rs+" & "+unsignedImmediate+";");
+                p( "r"+rt+" = r"+rs+" & "+unsignedImmediate+";");
                 break;
             case 13: // ORI
-                emit(pc, "r"+rt+" = r"+rs+" | "+unsignedImmediate+";");
+                p( "r"+rt+" = r"+rs+" | "+unsignedImmediate+";");
                 break;
             case 14: // XORI
-                emit(pc, "r"+rt+" = r"+rs+" ^ "+unsignedImmediate+";");
+                p( "r"+rt+" = r"+rs+" ^ "+unsignedImmediate+";");
                 break;
             case 15: // LUI
-                emit(pc, "r"+rt+" = "+unsignedImmediate+" << 16;");
+                p( "r"+rt+" = "+unsignedImmediate+" << 16;");
                 break;
             case 16:
-                throw new RuntimeException("TLB/Exception support not implemented");
+                throw new CompilationException("TLB/Exception support not implemented");
             case 17: { // FPU
                 switch(rs) {
                     case 0: // MFC.1
-                        emit(pc, "r"+rt+" = f"+rd);
+                        p( "r"+rt+" = f"+rd+";");
                         break;
                     case 2: // CFC.1
-                        if(fs != 31) throw new EmulationException("FCR " + fs + " unavailable");
-                        emit(pc, "r"+rt+" = fcsr;");
+                        if(fs != 31) throw new CompilationException("FCR " + fs + " unavailable");
+                        p( "r"+rt+" = fcsr;");
                         break;
                     case 4: // MTC.1
-                        emit(pc, "f"+rd+" = r"+rt+";");
+                        p( "f"+rd+" = r"+rt+";");
                         break;
                     case 6: // CTC.1
-                        if(fs != 31) throw new EmulationException("FCR " + fs + " unavailable");
-                        emit(pc, "fcsr = r"+rt+";  ;") 
-                        break;
-                    case 8: // BC1F, BC1T
-                        emit(pc, "if(((fcsr&0x800000)!=0) == (((insn>>>16)&1)!=0)) {"+
-                             " pc += 4; int tmp = pc + "+branchTarget+"*4; nextPC = tmp; ;")}
+                        if(fs != 31) throw new CompilationException("FCR " + fs + " unavailable");
+                        p( "fcsr = r"+rt+";");
+                        break;
+                    case 8: {// BC1F, BC1T
+                        tmp = (insn>>>16)&1;
+                        p("//BC1F, BC1T");
+                        p("if(((fcsr&0x800000)!=0) == (" + tmp + "!=0)) {");
+                            indent++;
+                            emitInstruction(-1,nextInsn,-1);
+                            branch(pc,pc+branchTarget*4+4);
+                            indent--;
+                        p("}");
                         break;
-                    case 16: {  // Single
+                    }
+                    case 16: {  // Single 
                         switch(subcode) {
                             case 0: // ADD.S
-                                emit(pc, setFloat(fd,getFloat(fs)+"+"+getFloat(ft)));
+                                p(setFloat(fd,getFloat(fs)+"+"+getFloat(ft)));
                                 break;
                             case 1: // SUB.S
-                                emit(pc, setFloat(fd,getFloat(fs)+"-"+getFloat(ft)));
+                                p(setFloat(fd,getFloat(fs)+"-"+getFloat(ft)));
                                 break;
                             case 2: // MUL.S
-                                emit(pc, setFloat(fd,getFloat(fs)+"*"+getFloat(ft)));
+                                p(setFloat(fd,getFloat(fs)+"*"+getFloat(ft)));
                                 break;
                             case 3: // DIV.S
-                                emit(pc, setFloat(fd,getFloat(fs)+"/"+getFloat(ft)));
+                                p(setFloat(fd,getFloat(fs)+"/"+getFloat(ft)));
                                 break;
                             case 5: // ABS.S
-                                emit(pc, setFloat(fd,Math.abs("+getFloat(fs)+")));
+                                p(setFloat(fd,"Math.abs("+getFloat(fs)+")"));
                                 break;
                             case 6: // MOV.S
-                                emit(pc, f"+fd+" = f"+fs+");
+                                p("f"+fd+" = f"+fs+"; // MOV.S");
                                 break;
                             case 7: // NEG.S
-                                emit(pc, setFloat(fd,"-1 * ("+getFloat(fs)+")")); // FIXME: just flip the sign bi)t
+                                p(setFloat(fd,"-"+getFloat(fs))); // FEATURE: just flip the sign bit
                                 break;
                             case 33: // CVT.D.S
-                                emit(pc, setDouble(fd,getFloat(fs)));
+                                p(setDouble(fd,"(float)"+getFloat(fs)));
                                 break;
-                            case 36: // CVT.W.S
-                                switch(roundingMode()) {
-                                    case 0: emit(pc, "f"+fd+" = (int)Math.floor("+getFloat(fs)+"+0.5f;"); break; // Round to nearest
-                                    case 1: emit(pc, "f"+fd+" = (int)"+getFloat(fs)+";"); break; // Round towards zero
-                                    case 2: emit(pc, "f"+fd+" = (int)Math.ceil("+getFloat(fs)+";"); break; // Round towards plus infinity
-                                    case 3: emit(pc, "f"+fd+" = (int)Math.floor("+getFloat(fs)+";"); break; // Round towards minus infinity
-                                }
+                            case 36: // CVT.W.D
+                                p("// CVT.W.D");
+                                p("switch(roundingMode()) {");
+                                    indent++;
+                                    p("case 0: f"+fd+" = (int)Math.floor("+getFloat(fs)+"+0.5); break; // Round to nearest");
+                                    p("case 1: f"+fd+" = (int)"+getFloat(fs)+"; break; // Round towards zero");
+                                    p("case 2: f"+fd+" = (int)Math.ceil("+getFloat(fs)+"); break; // Round towards plus infinity");
+                                    p("case 3: f"+fd+" = (int)Math.floor("+getFloat(fs)+"); break; // Round towards minus infinity");
+                                    indent--;
+                                p("}");
                                 break;
-                                /* FIXME: not implemented yet
-                            case -50: // C.EQ.S
-                                setFC("+getFloat(fs)+" == "+getFloat(ft)+"); // FIXME: just compare the ints, be sure things are normalized
+                            case 50: // C.EQ.D
+                                p("setFC("+getFloat(fs)+"=="+getFloat(ft)+");"); // FEATURE: just compare the ints, be sure things are normalized
                                 break;
-                            case 60: // C.LT.S
-                                setFC("+getFloat(fs)+" < "+getFloat(ft)+");
+                            case 60: // C.LT.D
+                                p("setFC("+getFloat(fs)+"<"+getFloat(ft)+");");
                                 break;
-                                */
-                            default: throw new RuntimeException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+                            case 62: // C.LE.D
+                                p("setFC("+getFloat(fs)+"<="+getFloat(ft)+");");
+                                break;                                
+                            default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
                         }
                         break;
                     }
                     case 17: { // Double
                         switch(subcode) {
                             case 0: // ADD.D
-                                emit(pc, "setDouble(fd,"+getDouble(fs)+"+"+getDouble(ft)+");");
+                                p(setDouble(fd,getDouble(fs)+"+"+getDouble(ft)));
                                 break;
                             case 1: // SUB.D
-                                emit(pc, "setDouble(fd,"+getDouble(fs)+"-"+getDouble(ft)+");");
+                                p(setDouble(fd,getDouble(fs)+"-"+getDouble(ft)));
                                 break;
                             case 2: // MUL.D
-                                emit(pc, "setDouble(fd,"+getDouble(fs)+"*"+getDouble(ft)+");");
+                                p(setDouble(fd,getDouble(fs)+"*"+getDouble(ft)));
                                 break;
                             case 3: // DIV.D
-                                emit(pc, "setDouble(fd,"+getDouble(fs)+"/"+getDouble(ft)+");");
+                                p(setDouble(fd,getDouble(fs)+"/"+getDouble(ft)));
                                 break;
                             case 5: // ABS.D
-                                emit(pc, "setDouble(fd,Math.abs("+getDouble(fs)+"));");
+                                p(setDouble(fd,"Math.abs("+getDouble(fs)+")"));
                                 break;
                             case 6: // MOV.D
-                                emit(pc, "f"+fd+" = f"+fs+";");
-                                emit(pc, "f"+fd+1+" = f"+fs+1+";");
+                                p("f"+fd+" = f"+fs+";");
+                                p("f"+(fd+1)+" = f"+(fs+1)+";");
                                 break;
                             case 7: // NEG.D
-                                emit(pc, "setDouble(fd,-"+getDouble(fs)+");"); // FIXME: just flip the sign bit"
+                                p(setDouble(fd,"-"+getDouble(fs))); // FEATURE: just flip the sign bit
                                 break;
                             case 32: // CVT.S.D
-                                emit(pc, "setFloat(fd,(float)"+getDouble(fs)+");");
+                                p(setFloat(fd,"(float)"+getDouble(fs)));
                                 break;
                             case 36: // CVT.W.D
-                                switch(roundingMode()) {
-                                    case 0: emit(pc, "f"+fd+" = (int)Math.floor("+getDouble(fs)+"+0.5);"); break; // Round to nearest
-                                    case 1: emit(pc, "f"+fd+" = (int)"+getDouble(fs)+";"); break; // Round towards zero
-                                    case 2: emit(pc, "f"+fd+" = (int)Math.ceil("+getDouble(fs)+");"); break; // Round towards plus infinity
-                                    case 3: emit(pc, "f"+fd+" = (int)Math.floor("+getDouble(fs)+");"); break; // Round towards minus infinity
-                                }
+                                p("// CVT.W.D");
+                                p("switch(roundingMode()) {");
+                                    indent++;
+                                    p("case 0: f"+fd+" = (int)Math.floor("+getDouble(fs)+"+0.5); break; // Round to nearest");
+                                    p("case 1: f"+fd+" = (int)"+getDouble(fs)+"; break; // Round towards zero");
+                                    p("case 2: f"+fd+" = (int)Math.ceil("+getDouble(fs)+"); break; // Round towards plus infinity");
+                                    p("case 3: f"+fd+" = (int)Math.floor("+getDouble(fs)+"); break; // Round towards minus infinity");
+                                    indent--;
+                                p("}");
                                 break;
-                                /* FIXME not implemented yet
                             case 50: // C.EQ.D
-                                setFC("+getDouble(fs)+" == "+getDouble(ft)+"); // FIXME: just compare the ints, be sure things are normalized
+                                p("setFC("+getDouble(fs)+"=="+getDouble(ft)+");"); // FEATURE: just compare the ints, be sure things are normalized
                                 break;
                             case 60: // C.LT.D
-                                setFC("+getDouble(fs)+" < "+getDouble(ft)+");
+                                p("setFC("+getDouble(fs)+"<"+getDouble(ft)+");");
                                 break;
                             case 62: // C.LE.D
-                                setFC("+getDouble(fs)+" <= "+getDouble(ft)+");
+                                p("setFC("+getDouble(fs)+"<="+getDouble(ft)+");");
                                 break;                                
-                                */
-                            default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+                            default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
                         }
                         break;
                     }
                     case 20: { // Integer
                         switch(subcode) {
+                            case 32: // CVT.S.W
+                                p(" // CVS.S.W");
+                                p(setFloat(fd,"((float)f"+fs+")"));
+                                break;
                             case 33: // CVT.D.W
-                                emit(pc, "setDouble(fd,(double)f"+fs+");");
+                                p("// CVT.D.W");
+                                p(setDouble(fd,"((double)f"+fs+")"));
                                 break;
-                            default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+                            default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
                         }
-                        break;
+                        break; 
                     }
                     default:
-                        throw new RuntimeException("Invalid Instruction 17/" + rs);
+                        throw new CompilationException("Invalid Instruction 17/" + rs);
+                }
+                break;
             }
             case 18: case 19:
-                throw new RuntimeException("No coprocessor installed");
+                throw new CompilationException("coprocessor 2 and 3 instructions not available");
             case 32: { // LB
-                    /* FIXME not done yet 
-                emit(pc, "addr = r"+rs+" + "+signedImmediate+";");
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&3) {
-                    case 0: int tmp = (tmp>>>24)&0xff; break;
-                    case 1: int tmp = (tmp>>>16)&0xff; break;
-                    case 2: int tmp = (tmp>>> 8)&0xff; break;
-                    case 3: int tmp = (tmp>>> 0)&0xff; break;
-                }
-                if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend
-                r"+rt+" = tmp;
-                    */
-                break;
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: tmp = (tmp>>>24)&0xff; break;");
+                    p("case 1: tmp = (tmp>>>16)&0xff; break;");
+                    p("case 2: tmp = (tmp>>> 8)&0xff; break;");
+                    p("case 3: tmp = (tmp>>> 0)&0xff; break;");
+                    indent--;
+                p("}");
+                p("if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend");
+                p("r"+rt+" = tmp;");
+                break; 
             }
             case 33: { // LH
-                /* FIXME not done yet
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&2) {
-                    case 0: int tmp = (tmp>>>16)&0xffff; break;
-                    case 2: int tmp = (tmp>>> 0)&0xffff; break;
-                }
-                if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend
-                r"+rt+" = tmp;
-                break;              
-                */
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&2) {");
+                    indent++;
+                    p("case 0: tmp = (tmp>>>16)&0xffff; break;");
+                    p("case 2: tmp = (tmp>>> 0)&0xffff; break;");
+                    indent--;
+                p("}");
+                p("if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend");
+                p("r"+rt+" = tmp;");
+                break; 
             }
-                /* FIXME not done yet
             case 34: { // LWL;
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&3) {
-                    case 0: r"+rt+" = (r"+rt+"&0x00000000)|(tmp<< 0); break;
-                    case 1: r"+rt+" = (r"+rt+"&0x000000ff)|(tmp<< 8); break;
-                    case 2: r"+rt+" = (r"+rt+"&0x0000ffff)|(tmp<<16); break;
-                    case 3: r"+rt+" = (r"+rt+"&0x00ffffff)|(tmp<<24); break;
-                }
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: r"+rt+" = (r"+rt+"&0x00000000)|(tmp<< 0); break;");
+                    p("case 1: r"+rt+" = (r"+rt+"&0x000000ff)|(tmp<< 8); break;");
+                    p("case 2: r"+rt+" = (r"+rt+"&0x0000ffff)|(tmp<<16); break;");
+                    p("case 3: r"+rt+" = (r"+rt+"&0x00ffffff)|(tmp<<24); break;");
+                    indent--;
+                p("}");
                 break;
             }
             case 35: // LW
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    r"+rt+" = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    r"+rt+" = memRead(addr);
-                }
+                memRead("r" + rs +"+"+signedImmediate,"r"+rt);
                 break;
             case 36: { // LBU
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr);
-                }
-                switch(addr&3) {
-                    case 0: r"+rt+" = (tmp>>>24)&0xff; break;
-                    case 1: r"+rt+" = (tmp>>>16)&0xff; break;
-                    case 2: r"+rt+" = (tmp>>> 8)&0xff; break;
-                    case 3: r"+rt+" = (tmp>>> 0)&0xff; break;
-                }
-                break;
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: r"+rt+" = (tmp>>>24)&0xff; break;");
+                    p("case 1: r"+rt+" = (tmp>>>16)&0xff; break;");
+                    p("case 2: r"+rt+" = (tmp>>> 8)&0xff; break;");
+                    p("case 3: r"+rt+" = (tmp>>> 0)&0xff; break;");
+                    indent--;
+                p("}");
+                break; 
             }
             case 37: { // LHU
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&2) {
-                    case 0: r"+rt+" = (tmp>>>16)&0xffff; break;
-                    case 2: r"+rt+" = (tmp>>> 0)&0xffff; break;
-                }
-                break;
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&2) {");
+                    indent++;
+                    p("case 0: r"+rt+" = (tmp>>>16)&0xffff; break;");
+                    p("case 2: r"+rt+" = (tmp>>> 0)&0xffff; break;");
+                    indent--;
+                p("}");
+                break; 
             }
             case 38: { // LWR
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&3) {
-                    case 0: r"+rt+" = (r"+rt+"&0xffffff00)|(tmp>>>24); break;
-                    case 1: r"+rt+" = (r"+rt+"&0xffff0000)|(tmp>>>16); break;
-                    case 2: r"+rt+" = (r"+rt+"&0xff000000)|(tmp>>> 8); break;
-                    case 3: r"+rt+" = (r"+rt+"&0x00000000)|(tmp>>> 0); break;
-                }
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: r"+rt+" = (r"+rt+"&0xffffff00)|(tmp>>>24); break;");
+                    p("case 1: r"+rt+" = (r"+rt+"&0xffff0000)|(tmp>>>16); break;");
+                    p("case 2: r"+rt+" = (r"+rt+"&0xff000000)|(tmp>>> 8); break;");
+                    p("case 3: r"+rt+" = (r"+rt+"&0x00000000)|(tmp>>> 0); break;");
+                    indent--;
+                p("}");
                 break;
             }
             case 40: { // SB
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&3) {
-                    case 0: int tmp = (tmp&0x00ffffff) | ((r"+rt+"&0xff)<<24); break;
-                    case 1: int tmp = (tmp&0xff00ffff) | ((r"+rt+"&0xff)<<16); break;
-                    case 2: int tmp = (tmp&0xffff00ff) | ((r"+rt+"&0xff)<< 8); break;
-                    case 3: int tmp = (tmp&0xffffff00) | ((r"+rt+"&0xff)<< 0); break;
-                }
-                try {
-                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
-                } catch(RuntimeException e) {
-                    memWrite(addr&~3,tmp);
-                }
+                p("// SB");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: tmp = (tmp&0x00ffffff) | ((r"+rt+"&0xff)<<24); break;");
+                    p("case 1: tmp = (tmp&0xff00ffff) | ((r"+rt+"&0xff)<<16); break;");
+                    p("case 2: tmp = (tmp&0xffff00ff) | ((r"+rt+"&0xff)<< 8); break;");
+                    p("case 3: tmp = (tmp&0xffffff00) | ((r"+rt+"&0xff)<< 0); break;");
+                    indent--;
+                p("}");
+                memWrite("addr&~3","tmp");
                 break;
             }
             case 41: { // SH
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
-                } catch(RuntimeException e) {
-                    int tmp = memRead(addr&~3);
-                }
-                switch(addr&2) {
-                    case 0: int tmp = (tmp&0x0000ffff) | ((r"+rt+"&0xffff)<<16); break;
-                    case 2: int tmp = (tmp&0xffff0000) | ((r"+rt+"&0xffff)<< 0); break;
-                }
-                try {
-                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
-                } catch(RuntimeException e) {
-                    memWrite(addr&~3,tmp);
-                }
+                p("// SH");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&2) {");
+                    indent++;
+                    p("case 0: tmp = (tmp&0x0000ffff) | ((r"+rt+"&0xffff)<<16); break;");
+                    p("case 2: tmp = (tmp&0xffff0000) | ((r"+rt+"&0xffff)<< 0); break;");
+                    indent--;
+                p("}");
+                memWrite("addr&~3","tmp");
                 break;
             }
             case 42: { // SWL
-                addr = r"+rs+" + signedImmediate;
-                int tmp = memRead(addr&~3);
-                switch(addr&3) {
-                    case 0: tmp=(tmp&0x00000000)|(r"+rt+">>> 0); break;
-                    case 1: tmp=(tmp&0xff000000)|(r"+rt+">>> 8); break;
-                    case 2: tmp=(tmp&0xffff0000)|(r"+rt+">>>16); break;
-                    case 3: tmp=(tmp&0xffffff00)|(r"+rt+">>>24); break;
-                }
-                try {
-                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
-                } catch(RuntimeException e) {
-                    memWrite(addr&~3,tmp);
-                }
+                p(" // SWL");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: tmp=(tmp&0x00000000)|(r"+rt+">>> 0); break;");
+                    p("case 1: tmp=(tmp&0xff000000)|(r"+rt+">>> 8); break;");
+                    p("case 2: tmp=(tmp&0xffff0000)|(r"+rt+">>>16); break;");
+                    p("case 3: tmp=(tmp&0xffffff00)|(r"+rt+">>>24); break;");
+                    indent--;
+                p("}");
+                memWrite("addr&~3","tmp");
                 break;
             }
             case 43: // SW
-                addr = r"+rs+" + signedImmediate;
-                try {
-                    writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = r"+rt+";
-                } catch(RuntimeException e) {
-                    memWrite(addr&~3,r"+rt+");
-                }
+                memWrite("r"+rs+"+"+signedImmediate,"r" + rt);
                 break;
             case 46: { // SWR
-                addr = r"+rs+" + signedImmediate;
-                int tmp = memRead(addr&~3);
-                switch(addr&3) {
-                    case 0: tmp=(tmp&0x00ffffff)|(r"+rt+"<<24); break;
-                    case 1: tmp=(tmp&0x0000ffff)|(r"+rt+"<<16); break;
-                    case 2: tmp=(tmp&0x000000ff)|(r"+rt+"<< 8); break;
-                    case 3: tmp=(tmp&0x00000000)|(r"+rt+"<< 0); break;
-                }
-                memWrite(addr&~3,tmp);
+                p(" // SWR");
+                p("addr=r" + rs +"+"+signedImmediate + ";");
+                memRead("addr&~3","tmp");
+                p("switch(addr&3) {");
+                    indent++;
+                    p("case 0: tmp=(tmp&0x00ffffff)|(r"+rt+"<<24); break;");
+                    p("case 1: tmp=(tmp&0x0000ffff)|(r"+rt+"<<16); break;");
+                    p("case 2: tmp=(tmp&0x000000ff)|(r"+rt+"<< 8); break;");
+                    p("case 3: tmp=(tmp&0x00000000)|(r"+rt+"<< 0); break;");
+                    indent--;
+                p("}");
+                memWrite("addr&~3","tmp");
                 break;
             }
             case 49: // LWC1
-                f"+rt+" = memRead(r"+rs+" + signedImmediate);
+                memRead("r"+rs+"+"+signedImmediate,"f"+rt);
                 break;
             case 57: // SWC1
-                memWrite(r"+rs+" + signedImmediate,f"+rt+");
+                memWrite("r"+rs+"+"+signedImmediate,"f"+rt);
                 break;
             default:
-                throw new EmulationException("Invalid Instruction: " + op);
-                */
+                throw new CompilationException("Invalid Instruction: " + op + " at " + toHex(pc));
         }
-        
     }
     
-    static String prefix = "";
-    static void emit(int vaddr, String s) {
-        if (s.indexOf("r0 = ") != -1) s = "    /* NOP */";
-        if (!s.trim().endsWith("return;") && s.indexOf("throw") == -1) s += " pc = 0x" + Long.toString((vaddr + 4) & 0xffffffffL,16) + ";";
-        System.out.println(s);
+    private static void memWrite(String addr, String target) {
+        if(fastMem)
+            p("writePages[("+addr+")>>>"+Runtime.PAGE_SHIFT+"][(("+addr+")>>>2)&"+toHex(Runtime.PAGE_WORDS-1)+"] = " + target + ";");
+        else
+            p("memWrite(" + addr + "," + target + ");");
+            
+    }
+    
+    private static void memRead(String addr, String target) {
+        if(fastMem)
+            p(target  + " = readPages[("+addr+")>>>"+Runtime.PAGE_SHIFT+"][(("+addr+")>>>2)&"+toHex(Runtime.PAGE_WORDS-1)+"];");
+        else
+            p(target + " = memRead(" + addr + ");");
     }
     
-    static String getFloat(int r) { return "Float.intBitsToFloat(f"+r+")"; }
-    static String getDouble(int r) {
-        return "Double.longBitsToDouble(((f"+r+"&0xffffffffL) << 32) | (f"+r+"&0xffffffffL));";
+    private static String getFloat(int r) { return "(Float.intBitsToFloat(f"+r+"))"; }
+    private static String getDouble(int r) {
+        return "(Double.longBitsToDouble(((f"+(r+1)+"&0xffffffffL) << 32) | (f"+r+"&0xffffffffL)))";
     }
-    static String setFloat(int r, String expr) { return "f"+r+" = Float.floatToRawIntBits("+expr+");" }
-    static String setDouble(int r, String expr) {
+    private static String setFloat(int r, String expr) { return "f"+r+"=Float.floatToRawIntBits("+expr+");"; }
+    private static String setDouble(int r, String expr) {
         return "{ long l = Double.doubleToLongBits("+expr+"); "+
-            "f"+(r+1)+" = (int)(l >>> 32); f"+(r)+" = (int)l; }";
+            "f"+(r+1)+" = (int)(l >>> 32); f"+r+" = (int)l; }";
+    }
+    
+    private final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
+    private final static String toHex8(int n) {
+        String s = Long.toString(n & 0xffffffffL, 16);
+        StringBuffer sb = new StringBuffer("0x");
+        for(int i=8-s.length();i>0;i--) sb.append('0');
+        sb.append(s);
+        return sb.toString();
     }
 }