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