a7fd8f2845e76b9192a5918c1f30114a20fed71b
[org.ibex.core.git] / src / org / xwt / mips / Compiler.java
1 // Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt.mips;
3
4 import java.util.*;
5 import java.io.*;
6
7 // FEATURE: progress indicator
8 // FEATURE: emit bytecode rather than .java code (for on-the-fly classloading without javac present in "real" JVMs
9
10 public class Compiler implements Registers {
11
12     private static StringBuffer runs = new StringBuffer();
13     private static StringBuffer inits = new StringBuffer();
14     
15     private static PrintStream out = System.out;
16     
17     private static int indent;
18     private static String indents[] = new String[16];
19     static { String s=""; for(int i=0;i<indents.length;i++,s=s+"    ") indents[i] = s; }
20     private static final void p() { out.println(); }
21     private static final void p(String s) { out.println(indents[indent] + s); }
22     
23     // FEATURE: This should probably provide some detail about where the excpetion happened (address, line numbers, etc)
24     private static class CompilationException extends Exception { public CompilationException(String s) { super(s); } }
25     
26     // Set this to true to enable fast memory access 
27     // When this is enabled a Java RuntimeException will be thrown when a page fault occures. When it is disabled
28     // a FaultException will be throw which is easier to catch and deal with, however. as the name implies, this is slower
29     private static boolean fastMem = true;
30     
31     // log_2 of the maximum bytes per method
32     // NOTE: This value can be much higher without breaking the classfile 
33     // specs (around 1024) but Hotstop seems to do much better with smaller
34     // methods. 
35     private static int LOG_MAX_BYTES_PER_METHOD = 9;
36     private static int MAX_BYTES_PER_METHOD = 512;
37     
38     // Store frequently used registers in local variables
39     // Unfortunately this doesn't seem to speed things up much
40     private static String[] freqRegs = { /*"r2", "r29", "r3", "r16", "r5", "r17", "r6", "r18", "r4", "r31", "r19"*/ };
41
42     // True to try to determine which case statement are needed and only include them
43     private static boolean pruneCases = true;
44     
45     // True to insert some code in the output to help diagnore compiler problems
46     private final static boolean debugCompiler = false;
47     
48     // True to print various statistics about the compilation
49     private final static boolean printStats = true;
50             
51     public static void main(String[] s) throws Exception {
52
53         if (s.length != 2) {
54             System.err.println("usage: java " + Compiler.class.getName() + " <classname> <binary.mips>");
55             System.exit(1);
56         }
57
58         String packageName = null;
59         String className = s[0];
60         if (s[0].indexOf('.') != -1) {
61             packageName = s[0].substring(0, s[0].lastIndexOf('.'));
62             className = s[0].substring(s[0].lastIndexOf('.') + 1);
63         }
64         
65         ELF elf = new ELF(s[1]);
66         if(elf.header.type != ELF.ELFHeader.ET_EXEC) throw new IOException("Binary is not an executable");
67         if(elf.header.machine != ELF.ELFHeader.EM_MIPS) throw new IOException("Binary is not for the MIPS I Architecture");
68
69         p("// This file was generated by MipsToJava");
70         if (packageName != null) p("package " + packageName + ";");
71         p("public class " + className + " extends org.xwt.mips.Runtime {");
72         p("");
73         p("    // program counter");
74         p("    private int pc = 0;");
75         if(debugCompiler)
76             p("    private int lastPC = 0;");
77         p("");
78         p("    // General Purpose registers");
79         p("    private final static int r0 = 0;");
80         p("    int r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0,");
81         p("        r8 = 0, r9 = 0, r10 = 0, r11 = 0, r12 = 0, r13 = 0, r14 = 0, r15 = 0,");
82         p("        r16 = 0, r17 = 0, r18 = 0, r19 = 0, r20 = 0, r21 = 0, r22 = 0, r23 = 0,");
83         p("        r24 = 0, r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0, r30 = 0, r31 = 0,");
84         p("        hi = 0, lo = 0;");
85         p("    // FP registers");
86         p("    private int f0 = 0, f1 = 0, f2 = 0, f3 = 0, f4 = 0, f5 = 0, f6 = 0, f7 = 0,");
87         p("        f8 = 0, f9 = 0, f10 = 0, f11 = 0, f12 = 0, f13 = 0, f14 = 0, f15 = 0,");
88         p("        f16 = 0, f17 = 0, f18 = 0, f19 = 0, f20 = 0, f21 = 0, f22 = 0, f23 = 0,");
89         p("        f24 = 0, f25 = 0, f26 = 0, f27 = 0, f28 = 0, f29 = 0, f30 = 0, f31 = 0;");
90         p("    // FP Control Register");        
91         p("    private int fcsr = 0;");
92         p("");
93         indent++;
94         // These should all be inlind by javac
95         p("private final void setFC(boolean b) { fcsr = (fcsr&~0x800000) | (b ? 0x800000 : 0x000000); }");
96         p("private final int roundingMode() { return fcsr & 3; /* bits 0-1 */ }");
97         indent--;
98         
99         Set jumpableAddresses = null;
100         if(pruneCases) {
101             // Find all possible branches
102             jumpableAddresses = new HashSet();
103             
104             jumpableAddresses.add(new Integer(elf.header.entry));
105             
106             ELF.SHeader text = elf.sectionWithName(".text");
107             if(text == null) throw new Error("No .text segment");
108             findBranchesInText(text.addr,new DataInputStream(text.getInputStream()),text.size,jumpableAddresses);
109             
110             findBranchesInSymtab(elf.getSymtab(),jumpableAddresses);            
111             
112             for(int i=0;i<elf.sheaders.length;i++) {
113                 ELF.SHeader sheader = elf.sheaders[i];
114                 String name = sheader.name;
115                 // if this section doesn't get loaded into our address space don't worry about it
116                 if(sheader.addr == 0x0) continue;
117                 if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
118                     findBranchesInData(new DataInputStream(sheader.getInputStream()),sheader.size,jumpableAddresses,text.addr,text.addr+text.size);
119             }
120         }
121         
122         // Generate main body functions
123         int highestAddr = 0;
124         indent=1;
125         for(int i=0;i<elf.sheaders.length;i++) {
126             ELF.SHeader sheader = elf.sheaders[i];
127             String name = sheader.name;
128             // if this section doesn't get loaded into our address space don't worry about it
129             if(sheader.addr == 0x0) continue;
130             
131             highestAddr = Math.max(highestAddr, sheader.addr + sheader.size);
132             
133             if(name.equals(".text")) {
134                 emitText(sheader.addr, new DataInputStream(sheader.getInputStream()),sheader.size,jumpableAddresses);
135                 endMethod(nextEmitTextAddr);
136             } else if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors")) {
137                 emitData(sheader.addr, new DataInputStream(sheader.getInputStream()), sheader.size,name.equals(".rodata")); 
138             } else if(name.equals(".bss") || name.equals(".sbss")) {
139                 if(sheader.entsize != 0) throw new CompilationException("bss segment has data!");
140                 emitBSS(sheader.addr,sheader.size);
141             } else {
142                 throw new CompilationException("Unknown segment: " + name);
143             }
144         }        
145         indent = 0;
146         
147         p("    public " + className + "() throws FaultException {");
148         if(fastMem) {
149             p("        super(false); // don't allow empty pages");
150             p("        if(PAGE_SIZE != " + toHex(Runtime.PAGE_SIZE) + ") throw new Error(\"Runtime.PAGE_SIZE mismatch\");");
151         } else {
152             p("        super(true); // allow empty pages");
153         }
154         p("        // init data");
155         p("        entryPoint = " + toHex(elf.header.entry) + ";");
156         p(inits.toString());
157         p("        brk = (" + toHex(highestAddr) + "+PAGE_SIZE-1) >>> PAGE_SHIFT;");
158         p("        state = INITIALIZED;");
159         p("    }");
160         p("");
161         p();
162         p("    public static void main(String[] javaArgs) throws Exception {");
163         p("        String[] args = new String[javaArgs.length+1];");
164         p("        System.arraycopy(javaArgs,0,args,1,javaArgs.length);");
165         p("        args[0] = \"" + className + "\";");
166         p("        " + className + " me = new " + className + "();");
167         p("        // User data");
168         p("        int addr = me.sbrk(PAGE_SIZE);");
169         p("        for(int i=0;i<10;i++) {");
170         p("            String s = \"User Info item: \" + (i+1) + \"\\0\";");
171         p("            byte[] b = s.getBytes(\"US-ASCII\");");
172         p("            me.copyout(b,addr,b.length);");
173         p("            me.setUserInfo(i,addr);");
174         p("            addr += b.length;");
175         p("        }");
176         p("        // End user data");
177         p("        int status = me.run(args);");
178         p("        System.err.println(\"Exit status: \" + status);");
179         p("        System.exit(status);");
180         p("        }");
181         p();
182         p("    protected void _start(int pc) {");
183         p("        // set the stack pointer");
184                 p("        r26 = STUFF_BASE;");
185                 p("        r27 = PAGE_SIZE;");
186         p("        r29 = INITIAL_SP;");
187         p("        // set the \"return address\" from _start to point at the \"magic exit address\" (0xdeadbeef)");
188         p("        r31 = 0xdeadbeef;");
189         p("        this.pc = pc;");
190         p("    }");
191
192         p();
193         p("    protected void _execute() throws ExecutionException { trampoline(); }");
194         p();
195         p("    private final void trampoline() throws ExecutionException {");
196         p("        boolean finished = false;");
197         p("        while(!finished) {");
198         p("            switch(this.pc >> " + LOG_MAX_BYTES_PER_METHOD + ") {");
199         p(runs.toString());
200         p("                default: throw new Error(\"invalid address 0x\" + Long.toString(this.pc&0xffffffffL,16));");
201         p("            }");
202         p("        }");
203         p("    }");
204         p("}");
205     }
206
207     private static int startOfMethod = 0;
208     private static int endOfMethod = 0;
209     
210     private static void startMethod(int addr) {
211         addr &= ~(MAX_BYTES_PER_METHOD-1);
212         endOfMethod= addr + MAX_BYTES_PER_METHOD;
213         String methodName = "run_" + Long.toString(addr & 0xffffffffL, 16);
214         runs.append(indents[4] + "case " + toHex(addr>>LOG_MAX_BYTES_PER_METHOD) + ": finished = !" + methodName + "(); break;\n");
215         p("private final boolean " + methodName + "() throws ExecutionException { /"+"* " + toHex(addr) + " - " + toHex(endOfMethod) + " *" + "/");
216         indent++;
217         p("int addr, tmp;");
218         for(int i=0;i<freqRegs.length;i++)
219             p("int " + freqRegs[i] + " = this." + freqRegs[i] + ";");
220         p("for(;;) {");
221         indent++;
222         p("switch(pc>>2) {");
223         indent++;
224         startOfMethod = addr;
225         
226     }
227     private static void endMethod() { endMethod(endOfMethod); }
228     private static void endMethod(int lastAddr) {
229         if(startOfMethod == 0) return;
230         // This isn't strictly necessary; its just here to work around unreachable code errors
231         p("case " + toHex(lastAddr>>2) + ":");
232         indent++;
233         p("pc=" + toHex(lastAddr) + ";");
234         leaveMethod();
235         indent--;
236         if(debugCompiler)
237             p("default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16)  + \" (got here from 0x\" + Long.toString(lastPC&0xffffffffL,16)+\")\");");
238         else
239             p("default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
240         indent--;
241         p("}"); // end switch
242         p("/* NOT REACHED */");
243         indent--;
244         p("}"); // end for
245         indent--;
246         p("}"); // end method
247         endOfMethod = 0;
248     }
249
250     private static void branch(int pc, int target) {
251         if(debugCompiler)
252             p("lastPC = " + toHex(pc) + ";");
253         p("pc=" + toHex(target) + ";");
254         if((pc>>LOG_MAX_BYTES_PER_METHOD) == (target>>LOG_MAX_BYTES_PER_METHOD))
255             p("continue;");
256         else
257             leaveMethod();
258     }
259     
260     private static void leaveMethod() { leaveMethod(true); }
261     private static void leaveMethod(boolean cont) {
262         for(int i=0;i<freqRegs.length;i++)
263             p("this." + freqRegs[i] + " = " + freqRegs[i] + ";");
264         p("return " + (cont?"true":"false") + ";");
265     }
266
267     private static int nextEmitTextAddr = -1;
268     private static void emitText(int addr, DataInputStream dis, int size, Set jumpableAddresses) throws CompilationException,IOException {
269         if(addr < nextEmitTextAddr) throw new CompilationException("Out of order sections");
270         if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
271         int count = size/4;
272         int nextInsn = dis.readInt();
273         if(nextInsn == -1) throw new Error("Actually read -1 at " + toHex(addr));
274         int insn;
275
276         for(int i=0;i<count;i++,addr+=4) {
277             insn = nextInsn;
278             nextInsn = (i == count-1) ? -1 : dis.readInt();
279             if(addr >= endOfMethod) { endMethod(); startMethod(addr); }
280             if(jumpableAddresses==null || addr == startOfMethod || jumpableAddresses.contains(new Integer(addr))) {
281                 p("case " + toHex(addr>>2) + ":");
282                 unreachable = false;
283             } else if(unreachable) {
284                 continue;
285             } else if(debugCompiler) {
286                 p("/" + "* pc = " + toHex(addr) + "*" + "/");
287             }
288             indent++;
289             emitInstruction(addr,insn,nextInsn);
290             indent--;
291         }
292         nextEmitTextAddr = addr;
293         dis.close();
294     }
295     
296     private static int initDataCount = 0;
297     private static void emitData(int addr, DataInputStream dis, int size, boolean readOnly) throws CompilationException,IOException {
298         if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
299         int count = size/4;
300         String varname =  "_data" + (++initDataCount);
301         p("private final static int[] " + varname +  " = {");
302         indent++;
303         for(int i=0;i<count;) {
304             StringBuffer sb = new StringBuffer();
305             for(int j=0;j<8 && i<count;j++,i++) {
306                 sb.append(toHex8(dis.readInt()));
307                 if(i!=count-1) sb.append(",");
308             }
309             p(sb.toString());
310         }
311         indent--;
312         p("};");
313         inits.append(indents[2] + "initPages(" + varname + "," + toHex(addr) + "," + (readOnly?"true":"false") + ");\n");
314         dis.close();
315     }
316     
317     private static int initBSSCount = 0;
318     private static void emitBSS(int addr, int size) throws CompilationException,IOException {
319         if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
320         int count = size/4;
321         inits.append(indents[2] + "clearPages(" + toHex(addr) + "," + toHex(count) + ");\n");
322     }
323     
324     private static void findBranchesInSymtab(ELF.Symtab symtab, Set jumps) {
325         ELF.Symbol[] symbols = symtab.symbols;
326         int n=0;
327         for(int i=0;i<symbols.length;i++) {
328             ELF.Symbol s = symbols[i];
329             if(s.type == ELF.Symbol.STT_FUNC) {
330                 //System.err.println("Adding symbol: " + s.name + " at " + toHex(s.addr));
331                 if(jumps.add(new Integer(s.addr))) n++;
332             }
333         }
334         if(printStats) System.err.println("Found " + n + " additional possible branch targets in Symtab");
335     }
336     
337     private static void findBranchesInText(int addr, DataInputStream dis, int size, Set jumps) throws IOException {
338         int count = size/4;
339         int pc = addr;
340         int n=0;
341         
342         for(int i=0;i<count;i++,pc+=4) {
343             int insn = dis.readInt();
344             int op = (insn >>> 26) & 0xff; 
345             int rs = (insn >>> 21) & 0x1f;
346             int rt = (insn >>> 16) & 0x1f;
347             int signedImmediate = (insn << 16) >> 16;
348             int branchTarget = signedImmediate;
349             int jumpTarget = (insn & 0x03ffffff);
350             int subcode = insn & 0x3f;
351             
352             switch(op) {
353                 case 0:
354                     switch(subcode) {
355                         case 9: // JALR
356                             if(jumps.add(new Integer(pc+8))) n++; // return address
357                             break;
358                         case 12: // SYSCALL
359                             if(jumps.add(new Integer(pc+4))) n++; 
360                             break;
361                     }
362                     break;
363                 case 1:
364                     switch(rt) {
365                         case 16: // BLTZAL
366                         case 17: // BGTZAL
367                             if(jumps.add(new Integer(pc+8))) n++; // return address
368                             // fall through
369                         case 0: // BLTZ
370                         case 1: // BGEZ
371                             if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
372                             break;
373                     }
374                     break;
375                 case 3: // JAL
376                     if(jumps.add(new Integer(pc+8))) n++; // return address
377                     // fall through
378                 case 2: // J
379                     if(jumps.add(new Integer((pc&0xf0000000)|(jumpTarget << 2)))) n++;
380                     break;
381                 case 4: // BEQ
382                 case 5: // BNE
383                 case 6: // BLEZ
384                 case 7: // BGTZ
385                     if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
386                     break;
387                 case 17: // FPU Instructions
388                     switch(rs) {
389                         case 8: // BC1F, BC1T
390                             if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
391                             break;
392                     }
393                     break;
394             }
395         }
396         dis.close();
397         if(printStats) System.err.println("Found " + n + " additional possible branch targets in Text segment");
398     }
399     
400     private static void findBranchesInData(DataInputStream dis, int size, Set jumps, int textStart, int textEnd) throws IOException {
401         int count = size/4;
402         int n=0;
403         for(int i=0;i<count;i++) {
404             int word = dis.readInt();
405             if((word&3)==0 && word >= textStart && word < textEnd) {
406                 if(jumps.add(new Integer(word))) n++;
407             }
408         }
409         dis.close();
410         if(n>0 && printStats) System.err.println("Found " + n + " additional possible branch targets in Data segment");
411     }
412     
413     private static boolean unreachable = false;
414     
415     private static void emitInstruction(int pc, int insn, int nextInsn) throws IOException,CompilationException {
416         if(insn == -1) throw new Error("insn is -1");
417         
418         int op = (insn >>> 26) & 0xff;                 // bits 26-31
419         int rs = (insn >>> 21) & 0x1f;                 // bits 21-25
420         int rt = (insn >>> 16) & 0x1f;                 // bits 16-20 
421         int ft = (insn >>> 16) & 0x1f;
422         int rd = (insn >>> 11) & 0x1f;                 // bits 11-15
423         int fs = (insn >>> 11) & 0x1f;
424         int shamt = (insn >>> 6) & 0x1f;               // bits 6-10
425         int fd = (insn >>> 6) & 0x1f;
426         int subcode = insn & 0x3f;                     // bits 0-5  
427
428         int jumpTarget = (insn & 0x03ffffff);          // bits 0-25
429         int unsignedImmediate = insn & 0xffff;
430         int signedImmediate = (insn << 16) >> 16;
431         int branchTarget = signedImmediate;
432
433         int tmp, addr; // temporaries
434         
435         //if(pc%64==0) p("System.err.println(\"Executing: " + toHex(pc) + "\");");
436         //p("/" + "*" + (pc == -1 ? "Delay Slot"  : toHex(pc)) + " *" + "/ ");
437         if(pc==-1) p("/" + "* Next insn is delay slot *" + "/ ");
438         
439         switch(op) {
440             case 0: {
441                 switch(subcode) {
442                     case 0: // SLL
443                         if(insn == 0) 
444                             p("/* NOOP */"); 
445                         else
446                             p( "r"+rd+" = r"+rt+" << "+shamt+";");
447                         break;
448                     case 2: // SRL
449                         p( "r"+rd+" = r"+rt+" >>> "+shamt+";");
450                         break;
451                     case 3: // SRA
452                         p( "r"+rd+" = r"+rt+" >> "+shamt+";");
453                         break;
454                     case 4: // SLLV
455                         p( "r"+rd+" = r"+rt+" << (r"+rs+"&0x1f);");
456                         break;
457                     case 6: // SRLV
458                         p( "r"+rd+" = r"+rt+" >>> (r"+rs+"&0x1f);");
459                         break;
460                     case 7: // SRAV
461                         p( "r"+rd+" = r"+rt+" >> (r"+rs+"&0x1f);");
462                         break;
463                     case 8: // JR
464                         if(pc == -1) throw new Error("pc modifying insn in delay slot");
465                         emitInstruction(-1,nextInsn,-1);
466                         if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
467                         p("pc=r" + rs + ";");
468                         leaveMethod();
469                         unreachable = true;
470                         break;
471                     case 9: // JALR
472                         if(pc == -1) throw new Error("pc modifying insn in delay slot");
473                         emitInstruction(-1,nextInsn,-1);
474                         if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
475                         p("pc=r" + rs + ";");
476                         p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
477                         leaveMethod();
478                         unreachable = true;
479                         break;
480                     case 12: // SYSCALL
481                         p( "r"+V0+" = syscall(r"+V0+",r"+A0+",r"+A1+",r"+A2+",r"+A3+");");
482                         p("if (state != RUNNING) {");
483                             indent++;
484                             p("pc = " + toHex(pc+4) + ";");
485                             leaveMethod(false);
486                             indent--;
487                         p("}");
488                         break;
489                     case 13: // BREAK
490                         p( "throw new ExecutionException(\"Break\");");
491                         break;
492                     case 16: // MFHI
493                         p( "r"+rd+" = hi;");
494                         break;
495                     case 17: // MTHI
496                         p( "hi = r"+rs+";");
497                         break;
498                     case 18: // MFLO
499                         p( "r"+rd+" = lo;");
500                         break;
501                     case 19: // MTLO
502                         p( "lo = r"+rs+";");
503                         break;
504                     case 24: // MULT
505                         p( "{ long hilo = (long)(r"+rs+") * ((long)r"+rt+"); " +
506                              "hi = (int) (hilo >>> 32); " +
507                              "lo = (int) hilo; }");
508                         break;
509                     case 25: // MULTU
510                         p( "{ long hilo = (r"+rs+" & 0xffffffffL) * (r"+rt+" & 0xffffffffL); " +
511                              "hi = (int) (hilo >>> 32); " +
512                              "lo = (int) hilo; } ");
513                         break;
514                     case 26: // DIV
515                         p( "hi = r"+rs+"%r"+rt+"; lo = r"+rs+"/r"+rt+";");
516                         break;
517                     case 27: // DIVU
518                         p( "hi = (int)((r"+rs+" & 0xffffffffL) % (r"+rt+" & 0xffffffffL)); " +
519                              "lo = (int)((r"+rs+" & 0xffffffffL) / (r"+rt+" & 0xffffffffL));");
520                         break;
521                     case 32: // ADD
522                         throw new CompilationException("ADD (add with oveflow trap) not suported");
523                         /*This must trap on overflow
524                         p( "r"+rd+" = r"+rs+" + r"+rt+";");
525                         break;*/
526                     case 33: // ADDU
527                         p( "r"+rd+" = r"+rs+" + r"+rt+";");
528                         break;
529                     case 34: // SUB
530                         throw new CompilationException("SUB (add with oveflow trap) not suported");
531                         /*This must trap on overflow
532                         p( "r"+rd+" = r"+rs+" - r"+rt+";");
533                         break;*/
534                     case 35: // SUBU
535                         p( "r"+rd+" = r"+rs+" - r"+rt+";");
536                         break;
537                     case 36: // AND
538                         p( "r"+rd+" = r"+rs+" & r"+rt+";");
539                         break;
540                     case 37: // OR
541                         p( "r"+rd+" = r"+rs+" | r"+rt+";");
542                         break;
543                     case 38: // XOR
544                         p( "r"+rd+" = r"+rs+" ^ r"+rt+";");
545                         break;
546                     case 39: // NOR
547                         p( "r"+rd+" = ~(r"+rs+" | r"+rt+");");
548                         break;
549                     case 42: // SLT
550                         p( "r"+rd+" = r"+rs+" < r"+rt+" ? 1 : 0;");
551                         break;
552                     case 43: // SLTU
553                         p( "r"+rd+" = ((r"+rs+" & 0xffffffffL) < (r"+rt+" & 0xffffffffL)) ? 1 : 0;");
554                         break;
555                     default:
556                         throw new RuntimeException("Illegal instruction 0/" + subcode);
557                 }
558                 break;
559             }
560             case 1: {
561                 switch(rt) {
562                     case 0: // BLTZ
563                         if(pc == -1) throw new Error("pc modifying insn in delay slot");
564                         p("if(r" + rs + " < 0) {");
565                             indent++;
566                             emitInstruction(-1,nextInsn,-1);
567                             branch(pc,pc+branchTarget*4+4);
568                             indent--;
569                         p("}");
570                         break;
571                     case 1: // BGEZ
572                         if(pc == -1) throw new Error("pc modifying insn in delay slot");
573                         p("if(r" + rs + " >= 0) {");
574                             indent++;
575                             emitInstruction(-1,nextInsn,-1);
576                             branch(pc,pc+branchTarget*4+4);
577                             indent--;
578                         p("}");
579                         break;
580                     case 16: // BLTZAL
581                         if(pc == -1) throw new Error("pc modifying insn in delay slot");
582                         p("if(r" + rs + " < 0) {");
583                             indent++;
584                             emitInstruction(-1,nextInsn,-1);
585                             p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
586                             branch(pc,pc+branchTarget*4+4);
587                             indent--;
588                         p("}");
589                         break;
590                     case 17: // BGEZAL
591                         if(pc == -1) throw new Error("pc modifying insn in delay slot");
592                         p("if(r" + rs + " >= 0) {");
593                             indent++;
594                             emitInstruction(-1,nextInsn,-1);
595                             p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
596                             branch(pc,pc+branchTarget*4+4);
597                             indent--;
598                         p("}");
599                         break;
600                     default:
601                         throw new RuntimeException("Illegal Instruction 1/" + rt);
602                 }
603                 break;
604             }
605             case 2: { // J
606                 if(pc == -1) throw new Error("pc modifying insn in delay slot");
607                 emitInstruction(-1,nextInsn,-1);
608                 branch(pc,(pc&0xf0000000)|(jumpTarget << 2));
609                 unreachable = true;
610                 break;
611             }
612             case 3: { // JAL
613                 if(pc == -1) throw new Error("pc modifying insn in delay slot");
614                 emitInstruction(-1,nextInsn,-1);
615                 p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
616                 branch(pc, (pc&0xf0000000)|(jumpTarget << 2));
617                 unreachable = true;
618                 break;
619             }
620             case 4: // BEQ
621                 if(pc == -1) throw new Error("pc modifying insn in delay slot");
622                 p("// BEQ");
623                 p("if(r" + rs + " == r" + rt + ") {");
624                     indent++;
625                     emitInstruction(-1,nextInsn,-1);
626                     branch(pc,pc+branchTarget*4+4);
627                     indent--;
628                 p("}");
629                 break;
630             case 5: // BNE       
631                 if(pc == -1) throw new Error("pc modifying insn in delay slot");
632                 p("if(r" + rs + " != r" + rt + ") {");
633                     indent++;
634                     emitInstruction(-1,nextInsn,-1);
635                     branch(pc,pc+branchTarget*4+4);
636                     indent--;
637                 p("}");
638                 break;
639             case 6: //BLEZ
640                 if(pc == -1) throw new Error("pc modifying insn in delay slot");
641                 p("if(r" + rs + " <= 0) {");
642                     indent++;
643                     emitInstruction(-1,nextInsn,-1);
644                     branch(pc,pc+branchTarget*4+4);
645                     indent--;
646                 p("}");
647                 break;
648             case 7: //BGTZ
649                 if(pc == -1) throw new Error("pc modifying insn in delay slot");
650                 p("if(r" + rs + " > 0) {");
651                     indent++;
652                     emitInstruction(-1,nextInsn,-1);
653                     branch(pc,pc+branchTarget*4+4);
654                     indent--;
655                 p("}");
656                 break;
657             case 8: // ADDI
658                 p( "r"+rt+" = r"+rs+" + "+signedImmediate +";");
659                 break;
660             case 9: // ADDIU
661                 p( "r"+rt+" = r"+rs+" + "+signedImmediate+";");
662                 break;
663             case 10: // SLTI
664                 p( "r"+rt+" = r"+rs+" < "+signedImmediate+" ? 1 : 0;");
665                 break;
666             case 11: // SLTIU
667                 p( "r"+rt+" = (r"+rs+"&0xffffffffL) < ("+unsignedImmediate+"&0xffffffffL) ? 1 : 0;");
668                 break;
669             case 12: // ANDI
670                 p( "r"+rt+" = r"+rs+" & "+unsignedImmediate+";");
671                 break;
672             case 13: // ORI
673                 p( "r"+rt+" = r"+rs+" | "+unsignedImmediate+";");
674                 break;
675             case 14: // XORI
676                 p( "r"+rt+" = r"+rs+" ^ "+unsignedImmediate+";");
677                 break;
678             case 15: // LUI
679                 p( "r"+rt+" = "+unsignedImmediate+" << 16;");
680                 break;
681             case 16:
682                 throw new CompilationException("TLB/Exception support not implemented");
683             case 17: { // FPU
684                 switch(rs) {
685                     case 0: // MFC.1
686                         p( "r"+rt+" = f"+rd+";");
687                         break;
688                     case 2: // CFC.1
689                         if(fs != 31) throw new CompilationException("FCR " + fs + " unavailable");
690                         p( "r"+rt+" = fcsr;");
691                         break;
692                     case 4: // MTC.1
693                         p( "f"+rd+" = r"+rt+";");
694                         break;
695                     case 6: // CTC.1
696                         if(fs != 31) throw new CompilationException("FCR " + fs + " unavailable");
697                         p( "fcsr = r"+rt+";");
698                         break;
699                     case 8: {// BC1F, BC1T
700                         tmp = (insn>>>16)&1;
701                         p("//BC1F, BC1T");
702                         p("if(((fcsr&0x800000)!=0) == (" + tmp + "!=0)) {");
703                             indent++;
704                             emitInstruction(-1,nextInsn,-1);
705                             branch(pc,pc+branchTarget*4+4);
706                             indent--;
707                         p("}");
708                         break;
709                     }
710                     case 16: {  // Single 
711                         switch(subcode) {
712                             case 0: // ADD.S
713                                 p(setFloat(fd,getFloat(fs)+"+"+getFloat(ft)));
714                                 break;
715                             case 1: // SUB.S
716                                 p(setFloat(fd,getFloat(fs)+"-"+getFloat(ft)));
717                                 break;
718                             case 2: // MUL.S
719                                 p(setFloat(fd,getFloat(fs)+"*"+getFloat(ft)));
720                                 break;
721                             case 3: // DIV.S
722                                 p(setFloat(fd,getFloat(fs)+"/"+getFloat(ft)));
723                                 break;
724                             case 5: // ABS.S
725                                 p(setFloat(fd,"Math.abs("+getFloat(fs)+")"));
726                                 break;
727                             case 6: // MOV.S
728                                 p("f"+fd+" = f"+fs+"; // MOV.S");
729                                 break;
730                             case 7: // NEG.S
731                                 p(setFloat(fd,"-"+getFloat(fs))); // FEATURE: just flip the sign bit
732                                 break;
733                             case 33: // CVT.D.S
734                                 p(setDouble(fd,"(float)"+getFloat(fs)));
735                                 break;
736                             case 36: // CVT.W.D
737                                 p("// CVT.W.D");
738                                 p("switch(roundingMode()) {");
739                                     indent++;
740                                     p("case 0: f"+fd+" = (int)Math.floor("+getFloat(fs)+"+0.5); break; // Round to nearest");
741                                     p("case 1: f"+fd+" = (int)"+getFloat(fs)+"; break; // Round towards zero");
742                                     p("case 2: f"+fd+" = (int)Math.ceil("+getFloat(fs)+"); break; // Round towards plus infinity");
743                                     p("case 3: f"+fd+" = (int)Math.floor("+getFloat(fs)+"); break; // Round towards minus infinity");
744                                     indent--;
745                                 p("}");
746                                 break;
747                             case 50: // C.EQ.D
748                                 p("setFC("+getFloat(fs)+"=="+getFloat(ft)+");"); // FEATURE: just compare the ints, be sure things are normalized
749                                 break;
750                             case 60: // C.LT.D
751                                 p("setFC("+getFloat(fs)+"<"+getFloat(ft)+");");
752                                 break;
753                             case 62: // C.LE.D
754                                 p("setFC("+getFloat(fs)+"<="+getFloat(ft)+");");
755                                 break;                                
756                             default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
757                         }
758                         break;
759                     }
760                     case 17: { // Double
761                         switch(subcode) {
762                             case 0: // ADD.D
763                                 p(setDouble(fd,getDouble(fs)+"+"+getDouble(ft)));
764                                 break;
765                             case 1: // SUB.D
766                                 p(setDouble(fd,getDouble(fs)+"-"+getDouble(ft)));
767                                 break;
768                             case 2: // MUL.D
769                                 p(setDouble(fd,getDouble(fs)+"*"+getDouble(ft)));
770                                 break;
771                             case 3: // DIV.D
772                                 p(setDouble(fd,getDouble(fs)+"/"+getDouble(ft)));
773                                 break;
774                             case 5: // ABS.D
775                                 p(setDouble(fd,"Math.abs("+getDouble(fs)+")"));
776                                 break;
777                             case 6: // MOV.D
778                                 p("f"+fd+" = f"+fs+";");
779                                 p("f"+(fd+1)+" = f"+(fs+1)+";");
780                                 break;
781                             case 7: // NEG.D
782                                 p(setDouble(fd,"-"+getDouble(fs))); // FEATURE: just flip the sign bit
783                                 break;
784                             case 32: // CVT.S.D
785                                 p(setFloat(fd,"(float)"+getDouble(fs)));
786                                 break;
787                             case 36: // CVT.W.D
788                                 p("// CVT.W.D");
789                                 p("switch(roundingMode()) {");
790                                     indent++;
791                                     p("case 0: f"+fd+" = (int)Math.floor("+getDouble(fs)+"+0.5); break; // Round to nearest");
792                                     p("case 1: f"+fd+" = (int)"+getDouble(fs)+"; break; // Round towards zero");
793                                     p("case 2: f"+fd+" = (int)Math.ceil("+getDouble(fs)+"); break; // Round towards plus infinity");
794                                     p("case 3: f"+fd+" = (int)Math.floor("+getDouble(fs)+"); break; // Round towards minus infinity");
795                                     indent--;
796                                 p("}");
797                                 break;
798                             case 50: // C.EQ.D
799                                 p("setFC("+getDouble(fs)+"=="+getDouble(ft)+");"); // FEATURE: just compare the ints, be sure things are normalized
800                                 break;
801                             case 60: // C.LT.D
802                                 p("setFC("+getDouble(fs)+"<"+getDouble(ft)+");");
803                                 break;
804                             case 62: // C.LE.D
805                                 p("setFC("+getDouble(fs)+"<="+getDouble(ft)+");");
806                                 break;                                
807                             default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
808                         }
809                         break;
810                     }
811                     case 20: { // Integer
812                         switch(subcode) {
813                             case 32: // CVT.S.W
814                                 p(" // CVS.S.W");
815                                 p(setFloat(fd,"((float)f"+fs+")"));
816                                 break;
817                             case 33: // CVT.D.W
818                                 p("// CVT.D.W");
819                                 p(setDouble(fd,"((double)f"+fs+")"));
820                                 break;
821                             default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
822                         }
823                         break; 
824                     }
825                     default:
826                         throw new CompilationException("Invalid Instruction 17/" + rs);
827                 }
828                 break;
829             }
830             case 18: case 19:
831                 throw new CompilationException("coprocessor 2 and 3 instructions not available");
832             case 32: { // LB
833                 p("addr=r" + rs +"+"+signedImmediate + ";");
834                 memRead("addr&~3","tmp");
835                 p("switch(addr&3) {");
836                     indent++;
837                     p("case 0: tmp = (tmp>>>24)&0xff; break;");
838                     p("case 1: tmp = (tmp>>>16)&0xff; break;");
839                     p("case 2: tmp = (tmp>>> 8)&0xff; break;");
840                     p("case 3: tmp = (tmp>>> 0)&0xff; break;");
841                     indent--;
842                 p("}");
843                 p("if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend");
844                 p("r"+rt+" = tmp;");
845                 break; 
846             }
847             case 33: { // LH
848                 p("addr=r" + rs +"+"+signedImmediate + ";");
849                 memRead("addr&~3","tmp");
850                 p("switch(addr&2) {");
851                     indent++;
852                     p("case 0: tmp = (tmp>>>16)&0xffff; break;");
853                     p("case 2: tmp = (tmp>>> 0)&0xffff; break;");
854                     indent--;
855                 p("}");
856                 p("if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend");
857                 p("r"+rt+" = tmp;");
858                 break; 
859             }
860             case 34: { // LWL;
861                 p("addr=r" + rs +"+"+signedImmediate + ";");
862                 memRead("addr&~3","tmp");
863                 p("switch(addr&3) {");
864                     indent++;
865                     p("case 0: r"+rt+" = (r"+rt+"&0x00000000)|(tmp<< 0); break;");
866                     p("case 1: r"+rt+" = (r"+rt+"&0x000000ff)|(tmp<< 8); break;");
867                     p("case 2: r"+rt+" = (r"+rt+"&0x0000ffff)|(tmp<<16); break;");
868                     p("case 3: r"+rt+" = (r"+rt+"&0x00ffffff)|(tmp<<24); break;");
869                     indent--;
870                 p("}");
871                 break;
872             }
873             case 35: // LW
874                 memRead("r" + rs +"+"+signedImmediate,"r"+rt);
875                 break;
876             case 36: { // LBU
877                 p("addr=r" + rs +"+"+signedImmediate + ";");
878                 memRead("addr&~3","tmp");
879                 p("switch(addr&3) {");
880                     indent++;
881                     p("case 0: r"+rt+" = (tmp>>>24)&0xff; break;");
882                     p("case 1: r"+rt+" = (tmp>>>16)&0xff; break;");
883                     p("case 2: r"+rt+" = (tmp>>> 8)&0xff; break;");
884                     p("case 3: r"+rt+" = (tmp>>> 0)&0xff; break;");
885                     indent--;
886                 p("}");
887                 break; 
888             }
889             case 37: { // LHU
890                 p("addr=r" + rs +"+"+signedImmediate + ";");
891                 memRead("addr&~3","tmp");
892                 p("switch(addr&2) {");
893                     indent++;
894                     p("case 0: r"+rt+" = (tmp>>>16)&0xffff; break;");
895                     p("case 2: r"+rt+" = (tmp>>> 0)&0xffff; break;");
896                     indent--;
897                 p("}");
898                 break; 
899             }
900             case 38: { // LWR
901                 p("addr=r" + rs +"+"+signedImmediate + ";");
902                 memRead("addr&~3","tmp");
903                 p("switch(addr&3) {");
904                     indent++;
905                     p("case 0: r"+rt+" = (r"+rt+"&0xffffff00)|(tmp>>>24); break;");
906                     p("case 1: r"+rt+" = (r"+rt+"&0xffff0000)|(tmp>>>16); break;");
907                     p("case 2: r"+rt+" = (r"+rt+"&0xff000000)|(tmp>>> 8); break;");
908                     p("case 3: r"+rt+" = (r"+rt+"&0x00000000)|(tmp>>> 0); break;");
909                     indent--;
910                 p("}");
911                 break;
912             }
913             case 40: { // SB
914                 p("// SB");
915                 p("addr=r" + rs +"+"+signedImmediate + ";");
916                 memRead("addr&~3","tmp");
917                 p("switch(addr&3) {");
918                     indent++;
919                     p("case 0: tmp = (tmp&0x00ffffff) | ((r"+rt+"&0xff)<<24); break;");
920                     p("case 1: tmp = (tmp&0xff00ffff) | ((r"+rt+"&0xff)<<16); break;");
921                     p("case 2: tmp = (tmp&0xffff00ff) | ((r"+rt+"&0xff)<< 8); break;");
922                     p("case 3: tmp = (tmp&0xffffff00) | ((r"+rt+"&0xff)<< 0); break;");
923                     indent--;
924                 p("}");
925                 memWrite("addr&~3","tmp");
926                 break;
927             }
928             case 41: { // SH
929                 p("// SH");
930                 p("addr=r" + rs +"+"+signedImmediate + ";");
931                 memRead("addr&~3","tmp");
932                 p("switch(addr&2) {");
933                     indent++;
934                     p("case 0: tmp = (tmp&0x0000ffff) | ((r"+rt+"&0xffff)<<16); break;");
935                     p("case 2: tmp = (tmp&0xffff0000) | ((r"+rt+"&0xffff)<< 0); break;");
936                     indent--;
937                 p("}");
938                 memWrite("addr&~3","tmp");
939                 break;
940             }
941             case 42: { // SWL
942                 p(" // SWL");
943                 p("addr=r" + rs +"+"+signedImmediate + ";");
944                 memRead("addr&~3","tmp");
945                 p("switch(addr&3) {");
946                     indent++;
947                     p("case 0: tmp=(tmp&0x00000000)|(r"+rt+">>> 0); break;");
948                     p("case 1: tmp=(tmp&0xff000000)|(r"+rt+">>> 8); break;");
949                     p("case 2: tmp=(tmp&0xffff0000)|(r"+rt+">>>16); break;");
950                     p("case 3: tmp=(tmp&0xffffff00)|(r"+rt+">>>24); break;");
951                     indent--;
952                 p("}");
953                 memWrite("addr&~3","tmp");
954                 break;
955             }
956             case 43: // SW
957                 memWrite("r"+rs+"+"+signedImmediate,"r" + rt);
958                 break;
959             case 46: { // SWR
960                 p(" // SWR");
961                 p("addr=r" + rs +"+"+signedImmediate + ";");
962                 memRead("addr&~3","tmp");
963                 p("switch(addr&3) {");
964                     indent++;
965                     p("case 0: tmp=(tmp&0x00ffffff)|(r"+rt+"<<24); break;");
966                     p("case 1: tmp=(tmp&0x0000ffff)|(r"+rt+"<<16); break;");
967                     p("case 2: tmp=(tmp&0x000000ff)|(r"+rt+"<< 8); break;");
968                     p("case 3: tmp=(tmp&0x00000000)|(r"+rt+"<< 0); break;");
969                     indent--;
970                 p("}");
971                 memWrite("addr&~3","tmp");
972                 break;
973             }
974             case 49: // LWC1
975                 memRead("r"+rs+"+"+signedImmediate,"f"+rt);
976                 break;
977             case 57: // SWC1
978                 memWrite("r"+rs+"+"+signedImmediate,"f"+rt);
979                 break;
980             default:
981                 throw new CompilationException("Invalid Instruction: " + op + " at " + toHex(pc));
982         }
983     }
984     
985     private static void memWrite(String addr, String target) {
986         if(fastMem)
987             p("writePages[("+addr+")>>>"+Runtime.PAGE_SHIFT+"][(("+addr+")>>>2)&"+toHex(Runtime.PAGE_WORDS-1)+"] = " + target + ";");
988         else
989             p("memWrite(" + addr + "," + target + ");");
990             
991     }
992     
993     private static void memRead(String addr, String target) {
994         if(fastMem)
995             p(target  + " = readPages[("+addr+")>>>"+Runtime.PAGE_SHIFT+"][(("+addr+")>>>2)&"+toHex(Runtime.PAGE_WORDS-1)+"];");
996         else
997             p(target + " = memRead(" + addr + ");");
998     }
999     
1000     private static String getFloat(int r) { return "(Float.intBitsToFloat(f"+r+"))"; }
1001     private static String getDouble(int r) {
1002         return "(Double.longBitsToDouble(((f"+(r+1)+"&0xffffffffL) << 32) | (f"+r+"&0xffffffffL)))";
1003     }
1004     private static String setFloat(int r, String expr) { return "f"+r+"=Float.floatToRawIntBits("+expr+");"; }
1005     private static String setDouble(int r, String expr) {
1006         return "{ long l = Double.doubleToLongBits("+expr+"); "+
1007             "f"+(r+1)+" = (int)(l >>> 32); f"+r+" = (int)l; }";
1008     }
1009     
1010     private final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
1011     private final static String toHex8(int n) {
1012         String s = Long.toString(n & 0xffffffffL, 16);
1013         StringBuffer sb = new StringBuffer("0x");
1014         for(int i=8-s.length();i>0;i--) sb.append('0');
1015         sb.append(s);
1016         return sb.toString();
1017     }
1018 }
1019