21fe7e5e80b1da2e075ead00d87c81421762a90b
[org.ibex.core.git] / src / org / xwt / mips / Compiler.java
1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt.mips;
3
4 import java.util.*;
5 import java.io.*;
6
7 // FIXME: lb/sb/sh/lh need not be word aligned
8 // FIXME: memory accesses aren't handling sign-extending properly
9 // FIXME: probably have to implement nonaligned access
10 // FIXME: implement malloc()
11 // FIXME: implement an ELF parser based on RandomAccessFile
12 // FEATURE: progress indicator
13 // FEATURE: support n32 abi (passes more arguments in registers)
14 // FEATURE: trap on arithmetic overflows
15 // FEATURE: FPU
16 // FEATURE: we always know the value of the pc register; we should emit it as a literal when it appears in computations
17 // FEATURE: emit bytecode rather than .java code (for on-the-fly classloading without javac present in "real" JVMs)
18
19 /** reads a fully linked MIPS ELF binary image on stdin; writes a .java file on stdout */
20 public class Compiler {
21
22     static String runs = "";
23     static int last_emit = -1;
24     static DataInputStream dis;
25     public static void main(String[] s) throws IOException {
26
27         if (s.length != 2) {
28             System.err.println("usage: java " + Compiler.class.getName() + " <classname> <binary.mips>");
29             System.exit(-1);
30         }
31
32         String packageName = null;
33         String className = s[0];
34         if (s[0].indexOf('.') != -1) {
35             packageName = s[0].substring(0, s[0].lastIndexOf('.'));
36             className = s[0].substring(s[0].lastIndexOf('.') + 1);
37         }
38
39         System.out.println(prefix + "// This file was generated by MipsToJava");
40         if (packageName != null) System.out.println(prefix + "package " + packageName + ";");
41         System.out.println(prefix + "public class " + className + " extends org.xwt.mips.VM {");
42         System.out.println(prefix + "");
43         System.out.println(prefix + "    public " + className + "() { }");
44         System.out.println(prefix + "");
45         System.out.println(prefix + "    // program counter");
46         System.out.println(prefix + "    int pc = 0;");
47         System.out.println(prefix + "");
48         System.out.println(prefix + "    // temporary");
49         System.out.println(prefix + "    int tmp = 0;");
50         System.out.println(prefix + "");
51         System.out.println(prefix + "    // MIPS multiply/divide subsystem; 64-bit result");
52         System.out.println(prefix + "    long hilo = 0;");
53         System.out.println(prefix + "");
54         System.out.println(prefix + "    // General Purpose registers");
55         System.out.println(prefix + "    final int r0 = 0;");
56         System.out.println(prefix + "    int r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0,");
57         System.out.println(prefix + "        r8 = 0, r9 = 0, r10 = 0, r11 = 0, r12 = 0, r13 = 0, r14 = 0, r15 = 0,");
58         System.out.println(prefix + "        r16 = 0, r17 = 0, r18 = 0, r19 = 0, r20 = 0, r21 = 0, r22 = 0, r23 = 0,");
59         System.out.println(prefix + "        r24 = 0, r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0, r30 = 0, r31 = 0;");
60         System.out.println(prefix + "");
61
62         ELF elf = new ELF(new RandomAccessFile(args[1], "r"));
63         if(elf.header.type != ELF.ELFHeader.ET_EXEC) throw new IOException("Binary is not an executable");
64         if(elf.header.machine != ELF.ELFHeader.EM_MIPS) throw new IOException("Binary is not for the MIPS I Architecture");
65
66         ELF.PHeader[] pheaders = elf.pheaders;
67
68         int[][] readPages = new int[TOTAL_PAGES][];
69         int[][] writePages = new int[TOTAL_PAGES][];
70         for(int i=0; i<STACK_PAGES; i++)
71             readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage;
72
73         // FIXME: what is this?
74         int brk = 0;
75         for(int i=0;i<pheaders.length;i++) {
76             ELF.PHeader ph = pheaders[i];
77             if(ph.type != ELF.PHeader.PT_LOAD) continue;
78             int memsize = ph.memsz;
79             int filesize = ph.filesz;
80             if(memsize == 0) continue;
81             if(memsize < 0) throw new IOException("pheader size too large");
82             int addr = ph.vaddr;
83             if(addr == 0x0) throw new IOException("pheader vaddr == 0x0");
84             if(addr+memsize >= (brk<<PAGE_SHIFT)) brk = (addr+memsize+PAGE_SIZE-1) >> PAGE_SHIFT;
85             // FIXME: set memsize, serialize readPages/writePages
86             for(int j=0;j<memsize+PAGE_SIZE-1;j+=PAGE_SIZE) {
87                 int page = (j+addr) >>> PAGE_SHIFT;
88                 if(readPages[page] == null) readPages[page] = new int[PAGE_WORDS];
89                 if(ph.writable()) writePages[page] = readPages[page];
90             }
91             if(filesize != 0) {
92                 filesize = filesize & ~3;
93                 DataInputStream dis = new DataInputStream(ph.getInputStream());
94                 do {
95                     if (ph.executable()) emit(addr, 1, dis);
96                     readPages[addr >>> PAGE_SHIFT][(addr >>> 2)&(PAGE_WORDS-1)] = dis.readInt();
97                     addr+=4;
98                     filesize-=4;
99                 } while(filesize > 0);
100                 dis.close();
101             }
102         }
103
104         // cap off the last method
105         emit(-1, -1, dis);
106
107         System.out.println(prefix + "    protected int state = INITIALIZED;");
108         System.out.println(prefix + "    protected int brk = " + brk + ";");
109
110         System.out.println();
111         System.out.println(prefix + "    public static void main(String[] s) { new " + className + "()._start(entryPoint); }");
112         System.out.println();
113         System.out.println(prefix + "    protected int entryPoint = " + elf.header.entry + ";");
114         System.out.println();
115         System.out.println(prefix + "    protected void _start(int pc) {");
116         System.out.println();
117         System.out.println(prefix + "        // set the stack pointer");
118         System.out.println(prefix + "        r29 = INITIAL_SP;");
119         System.out.println();
120         System.out.println(prefix + "        // set the \"return address\" from _start to point at the \"magic exit address\" (0xdeadbeef)");
121         System.out.println(prefix + "        r31 = 0xdeadbeef;");
122         System.out.println();
123         System.out.println(prefix + "        // read in the .data segment");
124         System.out.println(prefix + "        //initData();");
125         System.out.println();
126         System.out.println(prefix + "        trampoline(pc);");
127         System.out.println();
128         System.out.println(prefix + "    }");
129
130         System.out.println();
131         System.out.println(prefix + "    public void execute() throws EmulationException {");
132         System.out.println(prefix + "        if(state == PAUSED) state = RUNNING;");
133         System.out.println(prefix + "        if(state != RUNNING)");
134         System.out.println(prefix + "             throw new IllegalStateException(\"execute() called in inappropriate state\");");
135         System.out.println(prefix + "        trampoline(this.pc);");
136         System.out.println(prefix + "    }");
137         System.out.println();
138         System.out.println(prefix + "    public void trampoline(int pc) {");
139         System.out.println(prefix + "        this.pc = pc;");
140         System.out.println(prefix + "        while(true) {");
141         System.out.println(prefix + "            switch(this.pc & 0xffffff00) {");
142         System.out.println(prefix + "                case 0xdeadbe00: System.out.println(\"exiting with return value \" + r2); System.exit(r2); continue;");
143         System.out.print(runs);
144         System.out.println(prefix + "                default: throw new Error(\"invalid address 0x\" + Long.toString(this.pc&0xffffffffL,16));");
145         System.out.println(prefix + "            }");
146         System.out.println(prefix + "        }");
147         System.out.println(prefix + "    }");
148         System.out.println(prefix + "}");
149     }
150
151     static int _instruction;
152     static boolean readnext = true;
153
154     /** reads <tt>numbytes</tt> from the stream, emitting <tt>case</tt> blocks starting at vaddr <tt>ofs</tt> */
155     static void emit(int vaddr, int numbytes, DataInputStream dis) throws IOException {
156         if ((vaddr == -1 && numbytes == -1) || (last_emit != -1 && ((last_emit & 0xffffff00) != (vaddr & 0xffffff00)))) {
157             System.out.println(prefix + "                case 0x" + Long.toString((last_emit & 0xffffffffL) + 0x1000, 16) + ": return;");
158             System.out.println(prefix + "                default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
159             System.out.println(prefix + "        }");
160             System.out.println(prefix + "    }");
161             if (vaddr == -1 && numbytes == -1) return;
162         }
163         if (last_emit == -1 || ((last_emit & 0xffffff00) != (vaddr & 0xffffff00))) {
164             System.out.println("");
165             System.out.println(prefix + "    private void run_" + Long.toString(vaddr & 0xffffff00L,16) + "() {");
166             runs += "                case 0x" + Long.toString(vaddr & 0xffffff00L,16) +
167                 ": run_" + Long.toString(vaddr & 0xffffff00L,16) + "(); continue;\n";
168             System.out.println(prefix + "        switch(pc) {");
169         }
170         int ofs = vaddr;
171         try {
172             OUTER: for(ofs = vaddr; ofs < vaddr + numbytes; ofs+=4) {
173                 if (readnext) _instruction = dis.readInt();
174                 readnext = true;
175
176                 String istring = Long.toString(_instruction & 0xffffffffL, 16);
177                 while(istring.length() < 8) istring = "0" + istring;
178                 String ostring = Long.toString(ofs & 0xffffffffL, 16);
179                 while(ostring.length() < 8) ostring = "0" + ostring;
180                 System.out.print(prefix + "                /* " + istring + " */   case 0x" + ostring + ": System.out.println(\"pc=0x\" + Long.toString(pc&0xffffffffL,16));");
181
182                 emit_instruction(ofs, _instruction);
183             }
184         } catch (EOFException e) {
185             emit(ofs, "                // warning, reached EOF before section end");
186         } finally {
187             last_emit = ofs;
188         }  
189     }
190
191     private static void emit_instruction(int pc, int insn) throws IOException {
192         int op = (insn >>> 26) & 0xff;                 // bits 26-31
193         int rs = (insn >>> 21) & 0x1f;                 // bits 21-25
194         int rt = (insn >>> 16) & 0x1f;                 // bits 16-20 
195         int ft = (insn >>> 16) & 0x1f;
196         int rd = (insn >>> 11) & 0x1f;                 // bits 11-15
197         int fs = (insn >>> 11) & 0x1f;
198         int shamt = (insn >>> 6) & 0x1f;               // bits 6-10
199         int fd = (insn >>> 6) & 0x1f;
200         int subcode = insn & 0x3f;                     // bits 0-5  
201
202         int jumpTarget = (insn & 0x03ffffff);          // bits 0-25
203         int unsignedImmediate = insn & 0xffff;
204         int signedImmediate = (insn << 16) >> 16;
205         int branchTarget = signedImmediate;
206
207         int tmp, addr; // temporaries
208         
209         switch(op) {
210             case 0: {
211                 switch(subcode) {
212                     case 0: // SLL
213                         if(insn == 0) break;
214                         emit(pc, "r"+rd+" = r"+rt+" << "+shamt+";");
215                         break;
216                     case 2: // SRL
217                         emit(pc, "r"+rd+" = r"+rt+" >>> "+shamt+";");
218                         break;
219                     case 3: // SRA
220                         emit(pc, "r"+rd+" = r"+rt+" >> "+shamt+";");
221                         break;
222                         // FIXME: Do we need % 32 on the r"+rs+" ?
223                     case 4: // SLLV
224                         emit(pc, "r"+rd+" = r"+rt+" << r"+rs+";");
225                         break;
226                     case 6: // SRLV
227                         emit(pc, "r"+rd+" = r"+rt+" >>> r"+rs+";");
228                         break;
229                     case 7: // SRAV
230                         emit(pc, "r"+rd+" = r"+rt+" >> r"+rs+";");
231                         break;
232                     case 8: // JR
233                         emit(pc, "{ int tmp = r"+rs+"; pc += 4; nextPC = tmp; }");
234                         continue OUTER;
235                     case 9: // JALR
236                         emit(pc, "{ int tmp = r"+rs+"; pc += 4; r"+rd+" = pc+4; nextPC = tmp; }");
237                         continue OUTER;
238                     case 12: // SYSCALL
239                         emit(pc, "r"+V0+" = syscall(r"+V0+",r"+A0+",r"+A1+",r"+A2+",r"+A3+"); " +
240                              "if (state != RUNNING) { this.nextPC = nextPC; return; }");
241                         break;
242                     case 13: // BREAK
243                         emit(pc, "throw new EmulationException(\"Break\")");
244                         break;
245                     case 16: // MFHI
246                         emit(pc, "r"+rd+" = hi;");
247                         break;
248                     case 17: // MTHI
249                         emit(pc, "hi = r"+rs+";");
250                         break;
251                     case 18: // MFLO
252                         emit(pc, "r"+rd+" = lo;");
253                         break;
254                     case 19: // MTLO
255                         emit(pc, "lo = r"+rs+";");
256                         break;
257                     case 24: // MULT
258                         emit(pc, "long hilo = (long)(r"+rs+") * ((long)r"+rt+"); " +
259                              "hi = (int) (hilo >>> 32); " +
260                              "lo = (int) hilo;");
261                         break;
262                     case 25: // MULTU
263                         emit(pc, "long hilo = (r"+rs+" & 0xffffffffL) * (r"+rt+" & 0xffffffffL); " +
264                              "hi = (int) (hilo >>> 32); " +
265                              "lo = (int) hilo;");
266                     case 26: // DIV
267                         emit(pc, "hi = r"+rs+"%r"+rt+"; lo = r"+rs+"/r"+rt+";");
268                         break;
269                     case 27: // DIVU
270                         emit(pc, "hi = (int)((r"+rs+" & 0xffffffffL) % (r"+rt+" & 0xffffffffL)); " +
271                              "lo = (int)((r"+rs+" & 0xffffffffL) / (r"+rt+" & 0xffffffffL));");
272                         break;
273                     case 32: // ADD
274                         emit(pc, "r"+rd+" = r"+rs+" + r"+rt+";"); // FIXME: Trap on overflow
275                         break;
276                     case 33: // ADDU
277                         emit(pc, "r"+rd+" = r"+rs+" + r"+rt+";");
278                         break;
279                     case 34: // SUB
280                         emit(pc, "r"+rd+" = r"+rs+" - r"+rt+";");  // FIXME: Trap on overflow
281                         break;
282                     case 35: // SUBU
283                         emit(pc, "r"+rd+" = r"+rs+" - r"+rt+";");
284                         break;
285                     case 36: // AND
286                         emit(pc, "r"+rd+" = r"+rs+" & r"+rt+";");
287                         break;
288                     case 37: // OR
289                         emit(pc, "r"+rd+" = r"+rs+" | r"+rt+";");
290                         break;
291                     case 38: // XOR
292                         emit(pc, "r"+rd+" = r"+rs+" ^ r"+rt+";");
293                         break;
294                     case 39: // NOR
295                         emit(pc, "r"+rd+" = ~(r"+rs+" | r"+rt+");");
296                         break;
297                     case 42: // SLT
298                         emit(pc, "r"+rd+" = r"+rs+" < r"+rt+" ? 1 : 0;");
299                         break;
300                     case 43: // SLTU
301                         emit(pc, "r"+rd+" = ((r"+rs+" & 0xffffffffL) < (r"+rt+" & 0xffffffffL)) ? 1 : 0;");
302                         break;
303                     default:
304                         throw new RuntimeException("Illegal instruction 0/" + subcode);
305                 }
306                 break;
307             }
308             case 1: {
309                 switch(rt) {
310                     case 0: // BLTZ
311                         emit(pc, "if (r"+rs+"<0) { pc += 4; int tmp = pc + "+
312                              branchTarget+"*4; nextPC = tmp; }");
313                         break;
314                     case 1: // BGEZ
315                         emit(pc, "if (r"+rs+">=0) { pc += 4; int tmp = pc + "+
316                              branchTarget+"*4; nextPC = tmp; }");
317                         break;
318                     case 16: // BLTZAL
319                         emit(pc, "if(r"+rs+" < 0) { pc += 4; r"+RA+" = pc+4; int tmp = pc + "+
320                              branchTarget+"*4; nextPC = tmp; }");
321                         break;
322                     case 17: // BGEZAL
323                         emit(pc, "if(r"+rs+" >= 0) { pc += 4; r"+RA+" = pc+4; int tmp = pc + "+
324                              branchTarget+"*4; nextPC = tmp; }");
325                         break;
326                     default:
327                         throw new RuntimeException("Illegal Instruction");
328                 }
329                 break;
330             }
331             case 2: { // J
332                 emit(pc, "int tmp = (pc&0xf0000000) | ("+jumpTarget+" << 2); pc+=4; nextPC = tmp;");
333                 break;
334             }
335             case 3: { // JAL
336                 emit(pc, "int tmp = (pc&0xf0000000) | ("+jumpTarget+" << 2); pc+=4; r"+RA+
337                      " = pc+4; nextPC = tmp;");
338                 break;
339             }
340             case 4: // BEQ
341                 emit(pc, "if(r"+rs+" == r"+rt+") { pc += 4; int tmp = pc + "+
342                      branchTarget+"*4; nextPC = tmp; }");
343                 break;
344             case 5: // BNE                
345                 emit(pc, "if(r"+rs+" != r"+rt+") { pc += 4; int tmp = pc + "+
346                      branchTarget+"*4; nextPC = tmp; }");
347                 break;
348             case 6: //BLEZ
349                 emit(pc, "if(r"+rs+" <= 0) { pc += 4; int tmp = pc + "+
350                      branchTarget+"*4; nextPC = tmp; ;");
351                 break;
352             case 7: //BGTZ
353                 emit(pc, "if(r"+rs+" > 0) { pc += 4; int tmp = pc + "+branchTarget+"*4; nextPC = tmp; }");
354                 break;
355             case 8: // ADDI
356                 emit(pc, "r"+rt+" = r"+rs+" + "+signedImmediate +";");
357                 break;
358             case 9: // ADDIU
359                 emit(pc, "r"+rt+" = r"+rs+" + "+signedImmediate+";");
360                 break;
361             case 10: // SLTI
362                 emit(pc, "r"+rt+" = r"+rs+" < "+signedImmediate+" ? 1 : 0;");
363                 break;
364             case 11: // SLTIU
365                 emit(pc, "r"+rt+" = (r"+rs+"&0xffffffffL) < ("+unsignedImmediate+"&0xffffffffL) ? 1 : 0;");
366                 break;
367             case 12: // ANDI
368                 emit(pc, "r"+rt+" = r"+rs+" & "+unsignedImmediate+";");
369                 break;
370             case 13: // ORI
371                 emit(pc, "r"+rt+" = r"+rs+" | "+unsignedImmediate+";");
372                 break;
373             case 14: // XORI
374                 emit(pc, "r"+rt+" = r"+rs+" ^ "+unsignedImmediate+";");
375                 break;
376             case 15: // LUI
377                 emit(pc, "r"+rt+" = "+unsignedImmediate+" << 16;");
378                 break;
379             case 16:
380                 throw new RuntimeException("TLB/Exception support not implemented");
381             case 17: { // FPU
382                 switch(rs) {
383                     case 0: // MFC.1
384                         emit(pc, "r"+rt+" = f"+rd);
385                         break;
386                     case 2: // CFC.1
387                         if(fs != 31) throw new EmulationException("FCR " + fs + " unavailable");
388                         emit(pc, "r"+rt+" = fcsr;");
389                         break;
390                     case 4: // MTC.1
391                         emit(pc, "f"+rd+" = r"+rt+";");
392                         break;
393                     case 6: // CTC.1
394                         if(fs != 31) throw new EmulationException("FCR " + fs + " unavailable");
395                         emit(pc, "fcsr = r"+rt+";  ;") 
396                         break;
397                     case 8: // BC1F, BC1T
398                         emit(pc, "if(((fcsr&0x800000)!=0) == (((insn>>>16)&1)!=0)) {"+
399                              " pc += 4; int tmp = pc + "+branchTarget+"*4; nextPC = tmp; ;")}
400                         break;
401                     case 16: {  // Single
402                         switch(subcode) {
403                             case 0: // ADD.S
404                                 emit(pc, setFloat(fd,getFloat(fs)+"+"+getFloat(ft)));
405                                 break;
406                             case 1: // SUB.S
407                                 emit(pc, setFloat(fd,getFloat(fs)+"-"+getFloat(ft)));
408                                 break;
409                             case 2: // MUL.S
410                                 emit(pc, setFloat(fd,getFloat(fs)+"*"+getFloat(ft)));
411                                 break;
412                             case 3: // DIV.S
413                                 emit(pc, setFloat(fd,getFloat(fs)+"/"+getFloat(ft)));
414                                 break;
415                             case 5: // ABS.S
416                                 emit(pc, setFloat(fd,Math.abs("+getFloat(fs)+")));
417                                 break;
418                             case 6: // MOV.S
419                                 emit(pc, f"+fd+" = f"+fs+");
420                                 break;
421                             case 7: // NEG.S
422                                 emit(pc, setFloat(fd,"-1 * ("+getFloat(fs)+")")); // FIXME: just flip the sign bi)t
423                                 break;
424                             case 33: // CVT.D.S
425                                 emit(pc, setDouble(fd,getFloat(fs)));
426                                 break;
427                             case 36: // CVT.W.S
428                                 switch(roundingMode()) {
429                                     case 0: emit(pc, "f"+fd+" = (int)Math.floor("+getFloat(fs)+"+0.5f;"); break; // Round to nearest
430                                     case 1: emit(pc, "f"+fd+" = (int)"+getFloat(fs)+";"); break; // Round towards zero
431                                     case 2: emit(pc, "f"+fd+" = (int)Math.ceil("+getFloat(fs)+";"); break; // Round towards plus infinity
432                                     case 3: emit(pc, "f"+fd+" = (int)Math.floor("+getFloat(fs)+";"); break; // Round towards minus infinity
433                                 }
434                                 break;
435                                 /* FIXME: not implemented yet
436                             case -50: // C.EQ.S
437                                 setFC("+getFloat(fs)+" == "+getFloat(ft)+"); // FIXME: just compare the ints, be sure things are normalized
438                                 break;
439                             case 60: // C.LT.S
440                                 setFC("+getFloat(fs)+" < "+getFloat(ft)+");
441                                 break;
442                                 */
443                             default: throw new RuntimeException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
444                         }
445                         break;
446                     }
447                     case 17: { // Double
448                         switch(subcode) {
449                             case 0: // ADD.D
450                                 emit(pc, "setDouble(fd,"+getDouble(fs)+"+"+getDouble(ft)+");");
451                                 break;
452                             case 1: // SUB.D
453                                 emit(pc, "setDouble(fd,"+getDouble(fs)+"-"+getDouble(ft)+");");
454                                 break;
455                             case 2: // MUL.D
456                                 emit(pc, "setDouble(fd,"+getDouble(fs)+"*"+getDouble(ft)+");");
457                                 break;
458                             case 3: // DIV.D
459                                 emit(pc, "setDouble(fd,"+getDouble(fs)+"/"+getDouble(ft)+");");
460                                 break;
461                             case 5: // ABS.D
462                                 emit(pc, "setDouble(fd,Math.abs("+getDouble(fs)+"));");
463                                 break;
464                             case 6: // MOV.D
465                                 emit(pc, "f"+fd+" = f"+fs+";");
466                                 emit(pc, "f"+fd+1+" = f"+fs+1+";");
467                                 break;
468                             case 7: // NEG.D
469                                 emit(pc, "setDouble(fd,-"+getDouble(fs)+");"); // FIXME: just flip the sign bit"
470                                 break;
471                             case 32: // CVT.S.D
472                                 emit(pc, "setFloat(fd,(float)"+getDouble(fs)+");");
473                                 break;
474                             case 36: // CVT.W.D
475                                 switch(roundingMode()) {
476                                     case 0: emit(pc, "f"+fd+" = (int)Math.floor("+getDouble(fs)+"+0.5);"); break; // Round to nearest
477                                     case 1: emit(pc, "f"+fd+" = (int)"+getDouble(fs)+";"); break; // Round towards zero
478                                     case 2: emit(pc, "f"+fd+" = (int)Math.ceil("+getDouble(fs)+");"); break; // Round towards plus infinity
479                                     case 3: emit(pc, "f"+fd+" = (int)Math.floor("+getDouble(fs)+");"); break; // Round towards minus infinity
480                                 }
481                                 break;
482                                 /* FIXME not implemented yet
483                             case 50: // C.EQ.D
484                                 setFC("+getDouble(fs)+" == "+getDouble(ft)+"); // FIXME: just compare the ints, be sure things are normalized
485                                 break;
486                             case 60: // C.LT.D
487                                 setFC("+getDouble(fs)+" < "+getDouble(ft)+");
488                                 break;
489                             case 62: // C.LE.D
490                                 setFC("+getDouble(fs)+" <= "+getDouble(ft)+");
491                                 break;                                
492                                 */
493                             default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
494                         }
495                         break;
496                     }
497                     case 20: { // Integer
498                         switch(subcode) {
499                             case 33: // CVT.D.W
500                                 emit(pc, "setDouble(fd,(double)f"+fs+");");
501                                 break;
502                             default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
503                         }
504                         break;
505                     }
506                     default:
507                         throw new RuntimeException("Invalid Instruction 17/" + rs);
508             }
509             case 18: case 19:
510                 throw new RuntimeException("No coprocessor installed");
511             case 32: { // LB
512                     /* FIXME not done yet 
513                 emit(pc, "addr = r"+rs+" + "+signedImmediate+";");
514                 try {
515                     int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
516                 } catch(RuntimeException e) {
517                     int tmp = memRead(addr&~3);
518                 }
519                 switch(addr&3) {
520                     case 0: int tmp = (tmp>>>24)&0xff; break;
521                     case 1: int tmp = (tmp>>>16)&0xff; break;
522                     case 2: int tmp = (tmp>>> 8)&0xff; break;
523                     case 3: int tmp = (tmp>>> 0)&0xff; break;
524                 }
525                 if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend
526                 r"+rt+" = tmp;
527                     */
528                 break;
529             }
530             case 33: { // LH
531                 /* FIXME not done yet
532                 addr = r"+rs+" + signedImmediate;
533                 try {
534                     int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
535                 } catch(RuntimeException e) {
536                     int tmp = memRead(addr&~3);
537                 }
538                 switch(addr&2) {
539                     case 0: int tmp = (tmp>>>16)&0xffff; break;
540                     case 2: int tmp = (tmp>>> 0)&0xffff; break;
541                 }
542                 if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend
543                 r"+rt+" = tmp;
544                 break;              
545                 */
546             }
547                 /* FIXME not done yet
548             case 34: { // LWL;
549                 addr = r"+rs+" + signedImmediate;
550                 try {
551                     int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
552                 } catch(RuntimeException e) {
553                     int tmp = memRead(addr&~3);
554                 }
555                 switch(addr&3) {
556                     case 0: r"+rt+" = (r"+rt+"&0x00000000)|(tmp<< 0); break;
557                     case 1: r"+rt+" = (r"+rt+"&0x000000ff)|(tmp<< 8); break;
558                     case 2: r"+rt+" = (r"+rt+"&0x0000ffff)|(tmp<<16); break;
559                     case 3: r"+rt+" = (r"+rt+"&0x00ffffff)|(tmp<<24); break;
560                 }
561                 break;
562             }
563             case 35: // LW
564                 addr = r"+rs+" + signedImmediate;
565                 try {
566                     r"+rt+" = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
567                 } catch(RuntimeException e) {
568                     r"+rt+" = memRead(addr);
569                 }
570                 break;
571             case 36: { // LBU
572                 addr = r"+rs+" + signedImmediate;
573                 try {
574                     int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
575                 } catch(RuntimeException e) {
576                     int tmp = memRead(addr);
577                 }
578                 switch(addr&3) {
579                     case 0: r"+rt+" = (tmp>>>24)&0xff; break;
580                     case 1: r"+rt+" = (tmp>>>16)&0xff; break;
581                     case 2: r"+rt+" = (tmp>>> 8)&0xff; break;
582                     case 3: r"+rt+" = (tmp>>> 0)&0xff; break;
583                 }
584                 break;
585             }
586             case 37: { // LHU
587                 addr = r"+rs+" + signedImmediate;
588                 try {
589                     int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
590                 } catch(RuntimeException e) {
591                     int tmp = memRead(addr&~3);
592                 }
593                 switch(addr&2) {
594                     case 0: r"+rt+" = (tmp>>>16)&0xffff; break;
595                     case 2: r"+rt+" = (tmp>>> 0)&0xffff; break;
596                 }
597                 break;
598             }
599             case 38: { // LWR
600                 addr = r"+rs+" + signedImmediate;
601                 try {
602                     int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
603                 } catch(RuntimeException e) {
604                     int tmp = memRead(addr&~3);
605                 }
606                 switch(addr&3) {
607                     case 0: r"+rt+" = (r"+rt+"&0xffffff00)|(tmp>>>24); break;
608                     case 1: r"+rt+" = (r"+rt+"&0xffff0000)|(tmp>>>16); break;
609                     case 2: r"+rt+" = (r"+rt+"&0xff000000)|(tmp>>> 8); break;
610                     case 3: r"+rt+" = (r"+rt+"&0x00000000)|(tmp>>> 0); break;
611                 }
612                 break;
613             }
614             case 40: { // SB
615                 addr = r"+rs+" + signedImmediate;
616                 try {
617                     int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
618                 } catch(RuntimeException e) {
619                     int tmp = memRead(addr&~3);
620                 }
621                 switch(addr&3) {
622                     case 0: int tmp = (tmp&0x00ffffff) | ((r"+rt+"&0xff)<<24); break;
623                     case 1: int tmp = (tmp&0xff00ffff) | ((r"+rt+"&0xff)<<16); break;
624                     case 2: int tmp = (tmp&0xffff00ff) | ((r"+rt+"&0xff)<< 8); break;
625                     case 3: int tmp = (tmp&0xffffff00) | ((r"+rt+"&0xff)<< 0); break;
626                 }
627                 try {
628                     writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
629                 } catch(RuntimeException e) {
630                     memWrite(addr&~3,tmp);
631                 }
632                 break;
633             }
634             case 41: { // SH
635                 addr = r"+rs+" + signedImmediate;
636                 try {
637                     int tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
638                 } catch(RuntimeException e) {
639                     int tmp = memRead(addr&~3);
640                 }
641                 switch(addr&2) {
642                     case 0: int tmp = (tmp&0x0000ffff) | ((r"+rt+"&0xffff)<<16); break;
643                     case 2: int tmp = (tmp&0xffff0000) | ((r"+rt+"&0xffff)<< 0); break;
644                 }
645                 try {
646                     writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
647                 } catch(RuntimeException e) {
648                     memWrite(addr&~3,tmp);
649                 }
650                 break;
651             }
652             case 42: { // SWL
653                 addr = r"+rs+" + signedImmediate;
654                 int tmp = memRead(addr&~3);
655                 switch(addr&3) {
656                     case 0: tmp=(tmp&0x00000000)|(r"+rt+">>> 0); break;
657                     case 1: tmp=(tmp&0xff000000)|(r"+rt+">>> 8); break;
658                     case 2: tmp=(tmp&0xffff0000)|(r"+rt+">>>16); break;
659                     case 3: tmp=(tmp&0xffffff00)|(r"+rt+">>>24); break;
660                 }
661                 try {
662                     writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
663                 } catch(RuntimeException e) {
664                     memWrite(addr&~3,tmp);
665                 }
666                 break;
667             }
668             case 43: // SW
669                 addr = r"+rs+" + signedImmediate;
670                 try {
671                     writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = r"+rt+";
672                 } catch(RuntimeException e) {
673                     memWrite(addr&~3,r"+rt+");
674                 }
675                 break;
676             case 46: { // SWR
677                 addr = r"+rs+" + signedImmediate;
678                 int tmp = memRead(addr&~3);
679                 switch(addr&3) {
680                     case 0: tmp=(tmp&0x00ffffff)|(r"+rt+"<<24); break;
681                     case 1: tmp=(tmp&0x0000ffff)|(r"+rt+"<<16); break;
682                     case 2: tmp=(tmp&0x000000ff)|(r"+rt+"<< 8); break;
683                     case 3: tmp=(tmp&0x00000000)|(r"+rt+"<< 0); break;
684                 }
685                 memWrite(addr&~3,tmp);
686                 break;
687             }
688             case 49: // LWC1
689                 f"+rt+" = memRead(r"+rs+" + signedImmediate);
690                 break;
691             case 57: // SWC1
692                 memWrite(r"+rs+" + signedImmediate,f"+rt+");
693                 break;
694             default:
695                 throw new EmulationException("Invalid Instruction: " + op);
696                 */
697         }
698         
699     }
700     
701     static String prefix = "";
702     static void emit(int vaddr, String s) {
703         if (s.indexOf("r0 = ") != -1) s = "    /* NOP */";
704         if (!s.trim().endsWith("return;") && s.indexOf("throw") == -1) s += " pc = 0x" + Long.toString((vaddr + 4) & 0xffffffffL,16) + ";";
705         System.out.println(s);
706     }
707     
708     static String getFloat(int r) { return "Float.intBitsToFloat(f"+r+")"; }
709     static String getDouble(int r) {
710         return "Double.longBitsToDouble(((f"+r+"&0xffffffffL) << 32) | (f"+r+"&0xffffffffL));";
711     }
712     static String setFloat(int r, String expr) { return "f"+r+" = Float.floatToRawIntBits("+expr+");" }
713     static String setDouble(int r, String expr) {
714         return "{ long l = Double.doubleToLongBits("+expr+"); "+
715             "f"+(r+1)+" = (int)(l >>> 32); f"+(r)+" = (int)l; }";
716     }
717 }
718