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