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