596e15fb78c1535ddfc417e2e9c10900622966c2
[nestedvm.git] / src / org / ibex / nestedvm / JavaSourceCompiler.java
1 package org.ibex.nestedvm;
2
3 import java.util.*;
4 import java.io.*;
5 import org.ibex.nestedvm.util.*;
6
7 public class JavaSourceCompiler extends Compiler {
8     /** Stores the "case r XXX: ... run_YYYY();" blocks generated by the emitText method/ */
9     private StringBuffer runs = new StringBuffer();
10     /** Stores the "initData" and "cleadData" calls generated by the emitData and emitBSS methods */
11     private StringBuffer inits = new StringBuffer();
12     /** Stores lines to go in the class scope */
13     private StringBuffer classLevel = new StringBuffer();
14     
15     /** The stream to write the compiled output to */
16     private PrintWriter out;
17
18     /** Prints a blank line to the output stream */
19     private void p() { out.println(); }
20     /** prints the given string (indented by <i>indent</i>*4 spaces) to the output stream */ 
21     private void p(String s) { out.println(indents[indent] + s); }
22     private void pblock(StringBuffer sb) { out.print(sb.toString()); }
23     
24     /** Used by the p() method to add indentation */
25     private int indent;
26     
27     private static String indents[] = new String[16];
28     static { String s=""; for(int i=0;i<indents.length;i++,s=s+"    ") indents[i] = s; }
29     
30     public JavaSourceCompiler(Seekable binary, String className, Writer w)  throws IOException {
31         super(binary,className);
32         out = new PrintWriter(w);
33     }
34     
35     protected void _go() throws Exn, IOException {
36         if(singleFloat) throw new Exn("JavaSourceCompiler doesn't support singleFloat");
37         String packageName;
38         String className;
39         if (fullClassName.indexOf('.') != -1) {
40             packageName = fullClassName.substring(0, fullClassName.lastIndexOf('.'));
41             className = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
42         } else {
43             className = fullClassName;
44             packageName = null;
45         }
46         
47         p("/* This file was generated from " + source + " by Mips2Java on " + dateTime() + " */");
48         if (packageName != null) p("package " + packageName + ";");
49         if(runtimeStats) p("import java.util.*;");
50         p();
51         p("public final class " + className + " extends " + runtimeClass + " {");
52         indent++;
53         
54         p("/* program counter */");
55         p("private int pc = 0;");
56         if(debugCompiler)
57             p("private int lastPC = 0;");
58         p();
59         p("/* General Purpose registers */");
60         p("private final static int r0 = 0;");
61         p("private int      r1,  r2,  r3,  r4,  r5,  r6,  r7,");
62         p("            r8,  r9,  r10, r11, r12, r13, r14, r15,");
63         p("            r16, r17, r18, r19, r20, r21, r22, r23,");
64         p("            r24, r25, r26, r27, r28, r29, r30, r31,");
65         p("            hi = 0, lo = 0;");
66         p("/* FP registers */");
67         p("private int f0,  f1,  f2,  f3,  f4,  f5,  f6,  f7,");
68         p("            f8,  f9,  f10, f11, f12, f13, f14, f15,");
69         p("            f16, f17, f18, f19, f20, f21, f22, f23,");
70         p("            f24, f25, f26, f27, f28, f29, f30, f31;");
71         p("/* FP Control Register */");
72         p("private int fcsr = 0;");
73         p();
74         
75         if(onePage) p("private final int[] page = readPages[0];");
76                 
77         // Generate main body functions (run_XXXX() blocks, _data[] arrays, etc) 
78         int highestAddr = 0;
79         
80         for(int i=0;i<elf.sheaders.length;i++) {
81             ELF.SHeader sheader = elf.sheaders[i];
82             String name = sheader.name;
83             // if this section doesn't get loaded into our address space don't worry about it
84             if(sheader.addr == 0x0) continue;
85             
86             highestAddr = Math.max(highestAddr, sheader.addr + sheader.size);
87             
88             if(name.equals(".text"))
89                 emitText(sheader.addr, new DataInputStream(sheader.getInputStream()),sheader.size);
90             else if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
91                 emitData(sheader.addr, new DataInputStream(sheader.getInputStream()), sheader.size,name.equals(".rodata")); 
92             else if(name.equals(".bss") || name.equals(".sbss"))                
93                 emitBSS(sheader.addr,sheader.size);
94             else
95                 throw new Exn("Unknown segment: " + name);
96         }
97         p();
98         
99         pblock(classLevel);
100         p();
101         
102         // Trampoline (dispatch calls to the appropriate run_XXX() methods
103         p("private final void trampoline() throws ExecutionException {");
104         indent++;
105         p("while(state == RUNNING) {");
106         indent++;
107         p("switch(pc>>>" + methodShift+ ") {");
108         //p("switch(pc&" + toHex(methodMask) + ") {");
109         indent++;
110         pblock(runs);
111         p("default: throw new ExecutionException(\"invalid address 0x\" + Long.toString(this.pc&0xffffffffL,16) + \": r2: \" + r2);");
112         indent--; p("}");
113         indent--; p("}");
114         indent--; p("}");
115         p();
116         
117         // Constructor
118         p("public " + className + "() {");
119         indent++;
120         p("super(" + pageSize + "," + totalPages + ");");
121         pblock(inits);
122         indent--;
123         p("}");
124         p();
125         
126         p("protected int entryPoint() { return " + toHex(elf.header.entry) + "; }");
127         p("protected int heapStart() { return " + toHex(highestAddr) + "; }");
128         p("protected int gp() { return " + toHex(gp.addr) + "; }");
129         if(userInfo != null) {
130             p("protected int userInfoBase() { return " + toHex(userInfo.addr) + "; }");            
131             p("protected int userInfoSize() { return " + toHex(userInfo.size) + "; }");            
132         }
133         
134         // main() function
135         p("public static void main(String[] args) throws Exception {");
136         indent++;
137         p("" + className + " me = new " + className + "();");
138         p("int status = me.run(\"" + fullClassName + "\",args);");
139         if(runtimeStats) p("me.printStats();");
140         p("System.exit(status);");
141         indent--;
142         p("}");
143         p();
144         
145         // Runtime abstract methods
146         p("protected void _execute() throws ExecutionException { trampoline(); }");
147         p();
148         
149         p("protected void setCPUState(CPUState state) {");
150         indent++;
151         for(int i=1;i<32;i++) p("r" + i + "=state.r[" + i + "];");
152         for(int i=0;i<32;i++) p("f" + i + "=state.f[" + i + "];");
153         p("hi=state.hi; lo=state.lo; fcsr=state.fcsr;");
154         p("pc=state.pc;");
155         indent--;
156         p("}");
157         p("protected void getCPUState(CPUState state) {");
158         indent++;
159         for(int i=1;i<32;i++) p("state.r[" + i + "]=r" + i+ ";");
160         for(int i=0;i<32;i++) p("state.f[" + i + "]=f" + i +";");
161         p("state.hi=hi; state.lo=lo; state.fcsr=fcsr;");
162         p("state.pc=pc;");
163         indent--;
164         p("}");
165         p();
166         
167         if(supportCall) {
168             p("private static final " + hashClass + " symbols = new " + hashClass + "();");
169             p("static {");
170             indent++;
171             ELF.Symbol[] symbols = elf.getSymtab().symbols;
172             for(int i=0;i<symbols.length;i++) {
173                 ELF.Symbol s = symbols[i];
174                 if(s.type == ELF.Symbol.STT_FUNC && s.binding == ELF.Symbol.STB_GLOBAL && (s.name.equals("_call_helper") || !s.name.startsWith("_")))
175                     p("symbols.put(\"" + s.name + "\",new Integer(" + toHex(s.addr) + "));");
176             }
177             indent--;
178             p("}");
179             p("public int lookupSymbol(String symbol) { Integer i = (Integer) symbols.get(symbol); return i==null ? -1 : i.intValue(); }");
180             p();
181         }
182         
183         // Runtime stats
184         if(runtimeStats) {
185             p("private HashMap counters = new HashMap();");
186             p("private void inc(String k) { Long i = (Long)counters.get(k); counters.put(k,new Long(i==null ? 1 : i.longValue() + 1)); }");
187             p("private void printStats() {");
188             p(" Iterator i = new TreeSet(counters.keySet()).iterator();");
189             p(" while(i.hasNext()) { Object o = i.next(); System.err.println(\"\" + o + \": \" + counters.get(o)); }");
190             p("}");
191             p();
192         }
193         
194         indent--;
195         p("}");
196     }
197     
198     private int startOfMethod = 0;
199     private int endOfMethod = 0;
200     
201     private void startMethod(int addr) {
202         addr &= ~(maxBytesPerMethod-1);
203         startOfMethod = addr;
204         endOfMethod = addr + maxBytesPerMethod;
205         String methodName = "run_" + Long.toString(addr & 0xffffffffL, 16);
206         runs.append(indents[4] + "case " + toHex(addr>>>methodShift) + ": " + methodName + "(); break; \n");
207         //runs.append(indents[4] + "case " + toHex(addr&methodMask) + ": " + methodName + "(); break; \n");
208         
209         p("private final void " + methodName + "() throws ExecutionException { /"+"* " + toHex(addr) + " - " + toHex(endOfMethod) + " *" + "/");
210         indent++;
211         p("int addr, tmp;");
212         p("for(;;) {");
213         indent++;
214         p("switch(pc) {");
215         indent++;
216     }
217     
218     private void endMethod() { endMethod(endOfMethod); }
219     private void endMethod(int lastAddr) {
220         if(startOfMethod == 0) return;
221         // We should be able to use if(!unreachable) here (i think)
222         // This isn't strictly necessary; its just here to work around unreachable code errors
223         p("case " + toHex(lastAddr) + ":");
224         indent++;
225         p("pc=" + constant(lastAddr) + ";");
226         leaveMethod();
227         indent--;
228         if(debugCompiler)
229             p("default: throw new ExecutionException(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16)  + \" (got here from 0x\" + Long.toString(lastPC&0xffffffffL,16)+\")\");");
230         else
231             p("default: throw new ExecutionException(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
232         indent--;
233         p("}"); // end switch
234         p("/* NOT REACHED */");
235         indent--;
236         p("}"); // end for
237         indent--;
238         p("}"); // end method
239         endOfMethod = startOfMethod = 0;
240     }
241     
242     private HashMap relativeAddrs = new HashMap();  
243     private String constant(int target) {
244         if(target >= 4096 && lessConstants) {
245             int n = target & ~1023;
246             String var = "N_" + toHex8(n);
247             if(relativeAddrs.get(new Integer(n)) == null) {
248                 relativeAddrs.put(new Integer(n),Boolean.TRUE);
249                 classLevel.append(indents[1] + "private static int " + var + " = " + toHex(n) + ";\n");
250             }
251             return "(" + var + " + " + toHex(target - n) + ")";
252         } else {
253             return toHex(target);
254         }
255     }
256     
257     private void branch(int pc, int target) {
258         if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
259         p("pc=" + constant(target) + ";");
260         if(target == 0)
261             p("throw new ExecutionException(\"Branch to addr 0x0\");");
262         else if((pc&methodMask) == (target&methodMask))
263             p("continue;");
264         else if(assumeTailCalls)
265             p("run_" +  Long.toString((target&methodMask)&0xffffffffL, 16) + "(); return;");
266         else
267             leaveMethod();
268     }
269     
270     private void leaveMethod() {
271         p("return;");
272     }
273     
274     private boolean textDone;
275     private void emitText(int addr, DataInputStream dis, int size) throws Exn,IOException {
276         if(textDone) throw new Exn("Multiple text segments");
277         textDone = true;
278         
279         if((addr&3)!=0 || (size&3)!=0) throw new Exn("Section on weird boundaries");
280         int count = size/4;
281         int nextInsn = dis.readInt();
282         if(nextInsn == -1) throw new Error("Actually read -1 at " + toHex(addr));
283         int insn;
284         
285         for(int i=0;i<count;i++,addr+=4) {
286             insn = nextInsn;
287             nextInsn = (i == count-1) ? -1 : dis.readInt();
288             if(addr >= endOfMethod) { endMethod(); startMethod(addr); }
289             if(jumpableAddresses==null || addr == startOfMethod || jumpableAddresses.contains(new Integer(addr))) {
290                 p("case " + toHex(addr) + ":");
291                 unreachable = false;
292             } else if(unreachable) {
293                 continue;
294             } else if(debugCompiler) {
295                 p("/" + "* pc = " + toHex(addr) + "*" + "/");
296             }
297             indent++;
298             emitInstruction(addr,insn,nextInsn);
299             indent--;
300         }
301         endMethod(addr);
302         p();
303         dis.close();
304     }
305     
306     private int initDataCount = 0;
307     private void emitData(int addr, DataInputStream dis, int size, boolean readOnly) throws Exn,IOException {
308         if((addr&3)!=0 || (size&3)!=0) throw new Exn("Data section on weird boundaries");
309         int last = addr + size;
310         while(addr < last) {
311             int segSize = Math.min(size,28000); // must be a multiple of 56
312             StringBuffer sb = new StringBuffer();
313             for(int i=0;i<segSize;i+=7) {
314                 long l = 0;
315                 for(int j=0;j<7;j++) {
316                     l <<= 8;
317                     byte b = (i+j < size) ? dis.readByte() : 1;
318                     l |= (b & 0xffL);
319                 }
320                 for(int j=0;j<8;j++) {
321                     char c = (char) ((l>>>(7*(7-j)))&0x7f);
322                     if(c=='\n') sb.append("\\n"); 
323                     else if(c=='\r') sb.append("\\r");
324                     else if(c=='\\') sb.append("\\\\");
325                     else if(c=='"') sb.append("\\\"");
326                     else if(c >= 32 && c <= 126) sb.append(c);
327                     else sb.append("\\" +  toOctal3(c));
328                 }
329             }
330             String varname =  "_data" + (++initDataCount);
331             p("private static final int[] " + varname + " = decodeData(\"" + sb.toString() + "\"," + toHex(segSize/4) + ");");
332             inits.append(indents[2] + "initPages(" + varname +"," + toHex(addr) + "," + (readOnly?"true":"false") + ");\n");
333             addr += segSize;
334             size -= segSize;
335         }
336         dis.close();
337     }
338
339     private void emitBSS(int addr, int size) throws Exn {
340         if((addr&3)!=0) throw new Exn("BSS section on weird boundaries");
341         size = (size+3)&~3;
342         int count = size/4;
343         inits.append(indents[2] + "clearPages(" + toHex(addr) + "," + toHex(count) + ");\n");
344     }
345
346     // True if the current code path is unreachable (any instruction with a case statement is reachable)
347     private boolean unreachable = false;
348     
349     private void emitInstruction(int pc, int insn, int nextInsn) throws IOException,Exn {
350         if(insn == -1) throw new Error("insn is -1");
351         
352         int op = (insn >>> 26) & 0xff;                 // bits 26-31
353         int rs = (insn >>> 21) & 0x1f;                 // bits 21-25
354         int rt = (insn >>> 16) & 0x1f;                 // bits 16-20 
355         int ft = (insn >>> 16) & 0x1f;
356         int rd = (insn >>> 11) & 0x1f;                 // bits 11-15
357         int fs = (insn >>> 11) & 0x1f;
358         int shamt = (insn >>> 6) & 0x1f;               // bits 6-10
359         int fd = (insn >>> 6) & 0x1f;
360         int subcode = insn & 0x3f;                     // bits 0-5  
361
362         int jumpTarget = (insn & 0x03ffffff);          // bits 0-25
363         int unsignedImmediate = insn & 0xffff;
364         int signedImmediate = (insn << 16) >> 16;
365         int branchTarget = signedImmediate;
366
367         int tmp; // temporaries
368         
369         //if(pc%64==0) p("System.err.println(\"Executing: " + toHex(pc) + "\");");
370         //p("/" + "*" + (pc == -1 ? "Delay Slot"  : toHex(pc)) + " *" + "/ ");
371         if(pc==-1) p("/" + "* Next insn is delay slot *" + "/ ");
372         
373         if(runtimeStats && op != 0) p("inc(\"opcode: " + op + "\");");
374         switch(op) {
375             case 0: {
376                 if(runtimeStats && insn != 0) p("inc(\"opcode: 0/" + subcode + "\");");
377                 switch(subcode) {
378                     case 0: // SLL
379                         if(insn != 0) 
380                             p( "r"+rd+" = r"+rt+" << "+shamt+";");
381                         break;
382                     case 2: // SRL
383                         p( "r"+rd+" = r"+rt+" >>> "+shamt+";");
384                         break;
385                     case 3: // SRA
386                         p( "r"+rd+" = r"+rt+" >> "+shamt+";");
387                         break;
388                     case 4: // SLLV
389                         p( "r"+rd+" = r"+rt+" << (r"+rs+"&0x1f);");
390                         break;
391                     case 6: // SRLV
392                         p( "r"+rd+" = r"+rt+" >>> (r"+rs+"&0x1f);");
393                         break;
394                     case 7: // SRAV
395                         p( "r"+rd+" = r"+rt+" >> (r"+rs+"&0x1f);");
396                         break;
397                     case 8: // JR
398                         if(pc == -1) throw new Error("pc modifying insn in delay slot");
399                         emitInstruction(-1,nextInsn,-1);
400                         if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
401                         p("pc=r" + rs + ";");
402                         leaveMethod();
403                         unreachable = true;
404                         break;
405                     case 9: // JALR
406                         if(pc == -1) throw new Error("pc modifying insn in delay slot");
407                         emitInstruction(-1,nextInsn,-1);
408                         if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
409                         p("pc=r" + rs + ";");
410                         p("r" + RA + "=" + constant(pc+8 /*skip this insn and delay slot*/) + ";");
411                         leaveMethod();
412                         unreachable = true;
413                         break;
414                     case 12: // SYSCALL
415                         p("pc = " + toHex(pc) + ";");
416                         p( "r"+V0+" = syscall(r"+V0+",r"+A0+",r"+A1+",r"+A2+",r"+A3+",r"+T0+",r"+T1+");");
417                         p("if (state != RUNNING) {");
418                             indent++;
419                             p("pc = " + toHex(pc+4) + ";");
420                             leaveMethod();
421                             indent--;
422                         p("}");
423                         break;
424                     case 13: // BREAK
425                         p( "throw new ExecutionException(\"Break\");");
426                         break;
427                     case 16: // MFHI
428                         p( "r"+rd+" = hi;");
429                         break;
430                     case 17: // MTHI
431                         p( "hi = r"+rs+";");
432                         break;
433                     case 18: // MFLO
434                         p( "r"+rd+" = lo;");
435                         break;
436                     case 19: // MTLO
437                         p( "lo = r"+rs+";");
438                         break;
439                     case 24: // MULT
440                         p( "{ long hilo = (long)(r"+rs+") * ((long)r"+rt+"); " +
441                              "hi = (int) (hilo >>> 32); " +
442                              "lo = (int) hilo; }");
443                         break;
444                     case 25: // MULTU
445                         p( "{ long hilo = (r"+rs+" & 0xffffffffL) * (r"+rt+" & 0xffffffffL); " +
446                              "hi = (int) (hilo >>> 32); " +
447                              "lo = (int) hilo; } ");
448                         break;
449                     case 26: // DIV
450                         p( "hi = r"+rs+"%r"+rt+"; lo = r"+rs+"/r"+rt+";");
451                         break;
452                     case 27: // DIVU
453                         p("if(r"+rt+"!=0) {");
454                         p( "hi = (int)((r"+rs+" & 0xffffffffL) % (r"+rt+" & 0xffffffffL)); " +
455                              "lo = (int)((r"+rs+" & 0xffffffffL) / (r"+rt+" & 0xffffffffL));");
456                         p("}");
457                         break;
458                     case 32: // ADD
459                          throw new Exn("ADD (add with oveflow trap) not suported");
460                         /*This must trap on overflow
461                         p( "r"+rd+" = r"+rs+" + r"+rt+";");
462                         break;*/
463                     case 33: // ADDU
464                         p( "r"+rd+" = r"+rs+" + r"+rt+";");
465                         break;
466                     case 34: // SUB
467                          throw new Exn("SUB (add with oveflow trap) not suported");
468                         /*This must trap on overflow
469                         p( "r"+rd+" = r"+rs+" - r"+rt+";");
470                         break;*/
471                     case 35: // SUBU
472                         p( "r"+rd+" = r"+rs+" - r"+rt+";");
473                         break;
474                     case 36: // AND
475                         p( "r"+rd+" = r"+rs+" & r"+rt+";");
476                         break;
477                     case 37: // OR
478                         p( "r"+rd+" = r"+rs+" | r"+rt+";");
479                         break;
480                     case 38: // XOR
481                         p( "r"+rd+" = r"+rs+" ^ r"+rt+";");
482                         break;
483                     case 39: // NOR
484                         p( "r"+rd+" = ~(r"+rs+" | r"+rt+");");
485                         break;
486                     case 42: // SLT
487                         p( "r"+rd+" = r"+rs+" < r"+rt+" ? 1 : 0;");
488                         break;
489                     case 43: // SLTU
490                         p( "r"+rd+" = ((r"+rs+" & 0xffffffffL) < (r"+rt+" & 0xffffffffL)) ? 1 : 0;");
491                         break;
492                     default:
493                         throw new RuntimeException("Illegal instruction 0/" + subcode);
494                 }
495                 break;
496             }
497             case 1: {
498                 switch(rt) {
499                     case 0: // BLTZ
500                         if(pc == -1) throw new Error("pc modifying insn in delay slot");
501                         p("if(r" + rs + " < 0) {");
502                             indent++;
503                             emitInstruction(-1,nextInsn,-1);
504                             branch(pc,pc+branchTarget*4+4);
505                             indent--;
506                         p("}");
507                         break;
508                     case 1: // BGEZ
509                         if(pc == -1) throw new Error("pc modifying insn in delay slot");
510                         p("if(r" + rs + " >= 0) {");
511                             indent++;
512                             emitInstruction(-1,nextInsn,-1);
513                             branch(pc,pc+branchTarget*4+4);
514                             indent--;
515                         p("}");
516                         break;
517                     case 16: // BLTZAL
518                         if(pc == -1) throw new Error("pc modifying insn in delay slot");
519                         p("if(r" + rs + " < 0) {");
520                             indent++;
521                             emitInstruction(-1,nextInsn,-1);
522                             p("r" + RA + "=" + constant(pc+8 /*skip this insn and delay slot*/) + ";");
523                             branch(pc,pc+branchTarget*4+4);
524                             indent--;
525                         p("}");
526                         break;
527                     case 17: // BGEZAL
528                         if(pc == -1) throw new Error("pc modifying insn in delay slot");
529                         p("if(r" + rs + " >= 0) {");
530                             indent++;
531                             emitInstruction(-1,nextInsn,-1);
532                             p("r" + RA + "=" + constant(pc+8 /*skip this insn and delay slot*/) + ";");
533                             branch(pc,pc+branchTarget*4+4);
534                             indent--;
535                         p("}");
536                         break;
537                     default:
538                         throw new RuntimeException("Illegal Instruction 1/" + rt);
539                 }
540                 break;
541             }
542             case 2: { // J
543                 if(pc == -1) throw new Error("pc modifying insn in delay slot");
544                 emitInstruction(-1,nextInsn,-1);
545                 branch(pc,(pc&0xf0000000)|(jumpTarget << 2));
546                 unreachable = true;
547                 break;
548             }
549             case 3: { // JAL
550                 if(pc == -1) throw new Error("pc modifying insn in delay slot");
551                 int target = (pc&0xf0000000)|(jumpTarget << 2);
552                 emitInstruction(-1,nextInsn,-1);
553                 p("r" + RA + "=" + constant(pc+8 /*skip this insn and delay slot*/) + ";");
554                 branch(pc, target);
555                 unreachable = true;
556                 break;
557             }
558             case 4: // BEQ
559                 if(pc == -1) throw new Error("pc modifying insn in delay slot");
560                 p("if(r" + rs + " == r" + rt + ") {");
561                     indent++;
562                     emitInstruction(-1,nextInsn,-1);
563                     branch(pc,pc+branchTarget*4+4);
564                     indent--;
565                 p("}");
566                 break;
567             case 5: // BNE       
568                 if(pc == -1) throw new Error("pc modifying insn in delay slot");
569                 p("if(r" + rs + " != r" + rt + ") {");
570                     indent++;
571                     emitInstruction(-1,nextInsn,-1);
572                     branch(pc,pc+branchTarget*4+4);
573                     indent--;
574                 p("}");
575                 break;
576             case 6: //BLEZ
577                 if(pc == -1) throw new Error("pc modifying insn in delay slot");
578                 p("if(r" + rs + " <= 0) {");
579                     indent++;
580                     emitInstruction(-1,nextInsn,-1);
581                     branch(pc,pc+branchTarget*4+4);
582                     indent--;
583                 p("}");
584                 break;
585             case 7: //BGTZ
586                 if(pc == -1) throw new Error("pc modifying insn in delay slot");
587                 p("if(r" + rs + " > 0) {");
588                     indent++;
589                     emitInstruction(-1,nextInsn,-1);
590                     branch(pc,pc+branchTarget*4+4);
591                     indent--;
592                 p("}");
593                 break;
594             case 8: // ADDI
595                 p( "r"+rt+" = r"+rs+" + "+signedImmediate +";");
596                 break;
597             case 9: // ADDIU
598                 p( "r"+rt+" = r"+rs+" + "+signedImmediate+";");
599                 break;
600             case 10: // SLTI
601                 p( "r"+rt+" = r"+rs+" < "+signedImmediate+" ? 1 : 0;");
602                 break;
603             case 11: // SLTIU
604                 p( "r"+rt+" = (r"+rs+"&0xffffffffL) < ("+signedImmediate+"&0xffffffffL) ? 1 : 0;");
605                 break;
606             case 12: // ANDI
607                 p( "r"+rt+" = r"+rs+" & "+unsignedImmediate+";");
608                 break;
609             case 13: // ORI
610                 p( "r"+rt+" = r"+rs+" | "+unsignedImmediate+";");
611                 break;
612             case 14: // XORI
613                 p( "r"+rt+" = r"+rs+" ^ "+unsignedImmediate+";");
614                 break;
615             case 15: // LUI
616                 p( "r"+rt+" = "+unsignedImmediate+" << 16;");
617                 break;
618             case 16:
619                 throw new Exn("TLB/Exception support not implemented");
620             case 17: { // FPU
621                 switch(rs) {
622                     case 0: // MFC.1
623                         p( "r"+rt+" = f"+rd+";");
624                         break;
625                     case 2: // CFC.1
626                         if(fs != 31) throw new Exn("FCR " + fs + " unavailable");
627                         p( "r"+rt+" = fcsr;");
628                         break;
629                     case 4: // MTC.1
630                         p( "f"+rd+" = r"+rt+";");
631                         break;
632                     case 6: // CTC.1
633                         if(fs != 31) throw new Exn("FCR " + fs + " unavailable");
634                         p( "fcsr = r"+rt+";");
635                         break;
636                     case 8: {// BC1F, BC1T
637                         tmp = (insn>>>16)&1;
638                         p("if(((fcsr&0x800000)!=0) == (" + tmp + "!=0)) {");
639                             indent++;
640                             emitInstruction(-1,nextInsn,-1);
641                             branch(pc,pc+branchTarget*4+4);
642                             indent--;
643                         p("}");
644                         break;
645                     }
646                     case 16: {  // Single 
647                         switch(subcode) {
648                             case 0: // ADD.S
649                                 p(setFloat(fd,getFloat(fs)+"+"+getFloat(ft)));
650                                 break;
651                             case 1: // SUB.S
652                                 p(setFloat(fd,getFloat(fs)+"-"+getFloat(ft)));
653                                 break;
654                             case 2: // MUL.S
655                                 p(setFloat(fd,getFloat(fs)+"*"+getFloat(ft)));
656                                 break;
657                             case 3: // DIV.S
658                                 p(setFloat(fd,getFloat(fs)+"/"+getFloat(ft)));
659                                 break;
660                             case 5: // ABS.S
661                                 p(setFloat(fd,"Math.abs("+getFloat(fs)+")"));
662                                 break;
663                             case 6: // MOV.S
664                                 p("f"+fd+" = f"+fs+"; // MOV.S");
665                                 break;
666                             case 7: // NEG.S
667                                 p(setFloat(fd,"-"+getFloat(fs)));
668                                 break;
669                             case 33: // CVT.D.S
670                                 p(setDouble(fd,"(float)"+getFloat(fs)));
671                                 break;
672                             case 36: // CVT.W.D
673                                 p("switch(fcsr & 3) {");
674                                     indent++;
675                                     p("case 0: f"+fd+" = (int)Math.floor("+getFloat(fs)+"+0.5); break; // Round to nearest");
676                                     p("case 1: f"+fd+" = (int)"+getFloat(fs)+"; break; // Round towards zero");
677                                     p("case 2: f"+fd+" = (int)Math.ceil("+getFloat(fs)+"); break; // Round towards plus infinity");
678                                     p("case 3: f"+fd+" = (int)Math.floor("+getFloat(fs)+"); break; // Round towards minus infinity");
679                                     indent--;
680                                 p("}");
681                                 break;
682                             case 50: // C.EQ.S
683                                 p("fcsr = (fcsr&~0x800000) | (("+getFloat(fs)+"=="+getFloat(ft)+") ? 0x800000 : 0x000000);");
684                                 break;
685                             case 60: // C.LT.S
686                                 p("fcsr = (fcsr&~0x800000) | (("+getFloat(fs)+"<"+getFloat(ft)+") ? 0x800000 : 0x000000);");
687                                 break;
688                             case 62: // C.LE.S
689                                 p("fcsr = (fcsr&~0x800000) | (("+getFloat(fs)+"<="+getFloat(ft)+") ? 0x800000 : 0x000000);");
690                                 break;                                
691                             default: throw new Exn("Invalid Instruction 17/" + rs + "/" + subcode);
692                         }
693                         break;
694                     }
695                     case 17: { // Double
696                         switch(subcode) {
697                             case 0: // ADD.D
698                                 p(setDouble(fd,getDouble(fs)+"+"+getDouble(ft)));
699                                 break;
700                             case 1: // SUB.D
701                                 p(setDouble(fd,getDouble(fs)+"-"+getDouble(ft)));
702                                 break;
703                             case 2: // MUL.D
704                                 p(setDouble(fd,getDouble(fs)+"*"+getDouble(ft)));
705                                 break;
706                             case 3: // DIV.D
707                                 p(setDouble(fd,getDouble(fs)+"/"+getDouble(ft)));
708                                 break;
709                             case 5: // ABS.D
710                                 p(setDouble(fd,"Math.abs("+getDouble(fs)+")"));
711                                 break;
712                             case 6: // MOV.D
713                                 p("f"+fd+" = f"+fs+";");
714                                 p("f"+(fd+1)+" = f"+(fs+1)+";");
715                                 break;
716                             case 7: // NEG.D
717                                 p(setDouble(fd,"-"+getDouble(fs)));
718                                 break;
719                             case 32: // CVT.S.D
720                                 p(setFloat(fd,"(float)"+getDouble(fs)));
721                                 break;
722                             case 36: // CVT.W.D
723                                 p("switch(fcsr & 3) {");
724                                     indent++;
725                                     p("case 0: f"+fd+" = (int)Math.floor("+getDouble(fs)+"+0.5); break; // Round to nearest");
726                                     p("case 1: f"+fd+" = (int)"+getDouble(fs)+"; break; // Round towards zero");
727                                     p("case 2: f"+fd+" = (int)Math.ceil("+getDouble(fs)+"); break; // Round towards plus infinity");
728                                     p("case 3: f"+fd+" = (int)Math.floor("+getDouble(fs)+"); break; // Round towards minus infinity");
729                                     indent--;
730                                 p("}");
731                                 break;
732                             case 50: // C.EQ.D
733                                 p("fcsr = (fcsr&~0x800000) | (("+getDouble(fs)+"=="+getDouble(ft)+") ? 0x800000 : 0x000000);");                                
734                                 break;
735                             case 60: // C.LT.D
736                                 p("fcsr = (fcsr&~0x800000) | (("+getDouble(fs)+"<"+getDouble(ft)+") ? 0x800000 : 0x000000);");                                
737                                 break;
738                             case 62: // C.LE.D
739                                 p("fcsr = (fcsr&~0x800000) | (("+getDouble(fs)+"<="+getDouble(ft)+") ? 0x800000 : 0x000000);");                                
740                                 break;                                
741                             default: throw new Exn("Invalid Instruction 17/" + rs + "/" + subcode);
742                         }
743                         break;
744                     }
745                     case 20: { // Integer
746                         switch(subcode) {
747                             case 32: // CVT.S.W
748                                 p(" // CVS.S.W");
749                                 p(setFloat(fd,"((float)f"+fs+")"));
750                                 break;
751                             case 33: // CVT.D.W
752                                 p(setDouble(fd,"((double)f"+fs+")"));
753                                 break;
754                             default: throw new Exn("Invalid Instruction 17/" + rs + "/" + subcode);
755                         }
756                         break; 
757                     }
758                     default:
759                         throw new Exn("Invalid Instruction 17/" + rs);
760                 }
761                 break;
762             }
763             case 18: case 19:
764                 throw new Exn("coprocessor 2 and 3 instructions not available");
765             case 32: { // LB
766                 if(runtimeStats) p("inc(\"LB\");");
767                 p("addr=r" + rs +"+"+signedImmediate + ";");
768                 memRead("addr","tmp");
769                 p("tmp = (tmp>>>(((~addr)&3)<<3)) & 0xff;");
770                 p("if((tmp&0x80)!=0) tmp |= 0xffffff00; /* sign extend */");
771                 p("r"+rt+" = tmp;");
772                 break; 
773             }
774             case 33: { // LH
775                 if(runtimeStats) p("inc(\"LH\");");
776                 p("addr=r" + rs +"+"+signedImmediate + ";");
777                 memRead("addr","tmp");
778                 p("tmp = (tmp>>>(((~addr)&2)<<3)) & 0xffff;");
779                 p("if((tmp&0x8000)!=0) tmp |= 0xffff0000; /* sign extend */");
780                 p("r"+rt+" = tmp;");
781                 break; 
782             }
783             case 34: { // LWL;
784                 p("addr=r" + rs +"+"+signedImmediate + ";");
785                 memRead("addr","tmp");
786                 p("r" + rt + " = (r"+rt+"&(0x00ffffff>>>(((~addr)&3)<<3)))|(tmp<<((addr&3)<<3));");
787                 break;
788                 /*p("addr=r" + rs +"+"+signedImmediate + ";");
789                 memRead("addr&~3","tmp");
790                 p("switch(addr&3) {");
791                 indent++;
792                 p("case 0: r"+rt+" = (r"+rt+"&0x00000000)|(tmp<< 0); break;");
793                 p("case 1: r"+rt+" = (r"+rt+"&0x000000ff)|(tmp<< 8); break;");
794                 p("case 2: r"+rt+" = (r"+rt+"&0x0000ffff)|(tmp<<16); break;");
795                 p("case 3: r"+rt+" = (r"+rt+"&0x00ffffff)|(tmp<<24); break;");
796                 indent--;
797                 p("}");
798                 break;*/
799             }
800             case 35: // LW
801                 if(runtimeStats) p("inc(\"LW\");");
802                 memRead("r" + rs +"+"+signedImmediate,"r"+rt);
803                 break;
804             case 36: { // LBU
805                 p("addr=r" + rs +"+"+signedImmediate + ";");
806                 memRead("addr","tmp");
807                 p("tmp = (tmp>>>(((~addr)&3)<<3)) & 0xff;");
808                 p("r"+rt+" = tmp;");
809                 break; 
810             }
811             case 37: { // LHU
812                 p("addr=r" + rs +"+"+signedImmediate + ";");
813                 memRead("addr","tmp");
814                 p("tmp = (tmp>>>(((~addr)&2)<<3)) & 0xffff;");
815                 p("r"+rt+" = tmp;");
816                 break; 
817             }
818             case 38: { // LWR
819                 p("addr=r" + rs +"+"+signedImmediate + ";");
820                 memRead("addr","tmp");
821                 p("r" + rt + " = (r"+rt+"&(0xffffff00<<((addr&3)<<3)))|(tmp>>>(((~addr)&3)<<3));");
822                 break;
823                 
824                 /*p("addr=r" + rs +"+"+signedImmediate + ";");
825                 memRead("addr&~3","tmp");
826                 p("switch(addr&3) {");
827                 indent++;
828                 p("case 0: r"+rt+" = (r"+rt+"&0xffffff00)|(tmp>>>24); break;");
829                 p("case 1: r"+rt+" = (r"+rt+"&0xffff0000)|(tmp>>>16); break;");
830                 p("case 2: r"+rt+" = (r"+rt+"&0xff000000)|(tmp>>> 8); break;");
831                 p("case 3: r"+rt+" = (r"+rt+"&0x00000000)|(tmp>>> 0); break;");
832                 indent--;
833                 p("}");
834                 break;*/
835                 
836             }
837             case 40: { // SB
838                 if(runtimeStats) p("inc(\"SB\");");
839                 p("addr=r" + rs +"+"+signedImmediate + ";");
840                 memRead("addr","tmp");
841                 p("tmp = (tmp&~(0xff000000>>>((addr&3)<<3)))|((r"+rt+"&0xff)<<(((~addr)&3)<<3));");
842                 memWrite("addr","tmp");
843                 break;
844             }
845             case 41: { // SH
846                 if(runtimeStats) p("inc(\"SH\");");
847                 p("addr=r" + rs +"+"+signedImmediate + ";");
848                 memRead("addr","tmp");
849                 p("tmp = (tmp&(0xffff<<((addr&2)<<3)))|((r" + rt + "&0xffff)<<(((~addr)&2)<<3));");
850                 memWrite("addr","tmp");
851                 break;
852             }
853             case 42: { // SWL
854                 p(" // SWL");
855                 p("addr=r" + rs +"+"+signedImmediate + ";");
856                 memRead("addr","tmp");
857                 p("tmp = (tmp&(0xffffff00<<(((~addr)&3)<<3)))|(r"+rt+">>>((addr&3)<<3));");
858                 memWrite("addr","tmp");
859                 break;
860             }
861             case 43: // SW
862                 if(runtimeStats) p("inc(\"SW\");");
863                 memWrite("r"+rs+"+"+signedImmediate,"r" + rt);
864                 break;
865             case 46: { // SWR
866                 p(" // SWR");
867                 p("addr=r" + rs +"+"+signedImmediate + ";");
868                 memRead("addr","tmp");
869                 p("tmp = (tmp&(0x00ffffff>>>((addr&3)<<3)))|(r"+rt+"<<(((~addr)&3)<<3));");
870                 memWrite("addr","tmp");
871                 break;
872             }
873             // Need to be atomic if threads
874             case 48: // LWC0/LL
875                 memRead("r"+rs+"+"+signedImmediate,"r"+rt);
876                 break;
877             case 49: // LWC1
878                 memRead("r"+rs+"+"+signedImmediate,"f"+rt);
879                 break;
880             // Needs to be atomic if threads
881             case 56: // SWC1/SC
882                 memWrite("r"+rs+"+"+signedImmediate,"r"+rt);
883                 p("r" + rt + "=1;");
884                 break;
885             case 57: // SWC1
886                 memWrite("r"+rs+"+"+signedImmediate,"f"+rt);
887                 break;
888             default:
889                 throw new Exn("Invalid Instruction: " + op + " at " + toHex(pc));
890         }
891     }
892     
893     // Helper functions for emitText
894     // NOTE: memWrite and memRead MUST discard the last two bits of addr
895     private void memWrite(String addr, String target) {
896         if(nullPointerCheck) p("nullPointerCheck(" + addr + ");");
897         if(onePage)
898             p("page[(" + addr + ")>>>2] = " + target + ";");
899         else if(fastMem)
900             p("writePages[("+addr+")>>>"+pageShift+"][(("+addr+")>>>2)&"+toHex((pageSize>>2)-1)+"] = " + target + ";");
901         else
902             p("unsafeMemWrite(" + addr + "," + target + ");");
903     }
904     private void memRead(String addr, String target) {
905         if(nullPointerCheck) p("nullPointerCheck(" + addr + ");");
906         if(onePage)
907             p(target + "= page[(" + addr + ")>>>2];");
908         else if(fastMem)
909             p(target  + " = readPages[("+addr+")>>>"+pageShift+"][(("+addr+")>>>2)&"+toHex((pageSize>>2)-1)+"];");
910         else
911             p(target + " = unsafeMemRead(" + addr + ");");
912     }
913     private static String getFloat(int r) { return "(Float.intBitsToFloat(f"+r+"))"; }
914     private static String getDouble(int r) {
915         return "(Double.longBitsToDouble(((f"+(r+1)+"&0xffffffffL) << 32) | (f"+r+"&0xffffffffL)))";
916     }
917     private static String setFloat(int r, String expr) { return "f"+r+"=Float.floatToRawIntBits("+expr+");"; }
918     private static String setDouble(int r, String expr) {
919         return "{ long l = Double.doubleToLongBits("+expr+"); "+
920             "f"+(r+1)+" = (int)(l >>> 32); f"+r+" = (int)l; }";
921     }
922 }
923