1 // Copyright 2003 Adam Megacz
3 // Based on org.xwt.mips.Compiler by Adam Megacz
8 public class Interpreter extends VM {
11 private int[] registers = new int[32];
14 // Floating Point Registers
15 private int[] fpregs = new int[32];
18 // 23 - conditional bit
20 // 12-17 - cause bits (unimplemented)
21 // 7-11 - enables bits (unimplemented)
22 // 2-6 - flags (unimplemented)
23 // 0-1 - rounding mode (only implemented for fixed point conversions)
28 // The filename if the binary we're running
31 // Register Operations
32 private final void setFC(boolean b) { fcsr = (fcsr&~0x800000) | (b ? 0x800000 : 0x000000); }
33 private final int roundingMode() { return fcsr & 3; /* bits 0-1 */ }
34 private final double getDouble(int r) {
35 return Double.longBitsToDouble(((fpregs[r+1]&0xffffffffL) << 32) | (fpregs[r]&0xffffffffL));
37 private final void setDouble(int r, double d) {
38 long l = Double.doubleToLongBits(d);
39 fpregs[r+1] = (int)(l >>> 32); fpregs[r] = (int)l;
41 private final float getFloat(int r) { return Float.intBitsToFloat(fpregs[r]); }
42 private final void setFloat(int r, float f) { fpregs[r] = Float.floatToRawIntBits(f); }
45 public void execute() throws EmulationException {
47 if(state == PAUSED) state = RUNNING;
48 if(state != RUNNING) throw new IllegalStateException("execute() called in inappropriate state");
52 // Main interpretor (also used for compilation)
53 // the return value is meaningless, its just to catch people typing "return" by accident
54 private int runSome(int pc) throws FaultException,EmulationException {
62 insn = readPages[pc>>>PAGE_SHIFT][(pc>>>2)&PAGE_WORDS-1];
63 } catch (RuntimeException e) {
67 int op = (insn >>> 26) & 0xff; // bits 26-31
68 int rs = (insn >>> 21) & 0x1f; // bits 21-25
69 int rt = (insn >>> 16) & 0x1f; // bits 16-20
70 int ft = (insn >>> 16) & 0x1f;
71 int rd = (insn >>> 11) & 0x1f; // bits 11-15
72 int fs = (insn >>> 11) & 0x1f;
73 int shamt = (insn >>> 6) & 0x1f; // bits 6-10
74 int fd = (insn >>> 6) & 0x1f;
75 int subcode = insn & 0x3f; // bits 0-5
77 int jumpTarget = (insn & 0x03ffffff); // bits 0-25
78 int unsignedImmediate = insn & 0xffff;
79 int signedImmediate = (insn << 16) >> 16;
80 int branchTarget = signedImmediate;
82 int tmp, addr; // temporaries
91 r[rd] = r[rt] << shamt;
94 r[rd] = r[rt] >>> shamt;
97 r[rd] = r[rt] >> shamt;
99 // FIXME: Do we need % 32 on the r[rs] ?
101 r[rd] = r[rt] << r[rs];
104 r[rd] = r[rt] >>> r[rs];
107 r[rd] = r[rt] >> r[rs];
110 tmp = r[rs]; pc += 4; nextPC = tmp;
113 tmp = r[rs]; pc += 4; r[rd] = pc+4; nextPC = tmp;
116 r[V0] = syscall(r[V0],r[A0],r[A1],r[A2],r[A3]);
117 if(state != RUNNING) {
118 this.nextPC = nextPC;
123 throw new EmulationException("Break");
137 long hilo = (long)(r[rs]) * ((long)r[rt]);
138 hi = (int) (hilo >>> 32);
143 long hilo = (r[rs] & 0xffffffffL) * (r[rt] & 0xffffffffL);
144 hi = (int) (hilo >>> 32);
153 hi = (int)((r[rs] & 0xffffffffL) % (r[rt] & 0xffffffffL));
154 lo = (int)((r[rs] & 0xffffffffL) / (r[rt] & 0xffffffffL));
157 r[rd] = r[rs] + r[rt]; // FIXME: Trap on overflow
160 r[rd] = r[rs] + r[rt];
163 r[rd] = r[rs] - r[rt]; // FIXME: Trap on overflow
166 r[rd] = r[rs] - r[rt];
169 r[rd] = r[rs] & r[rt];
172 r[rd] = r[rs] | r[rt];
175 r[rd] = r[rs] ^ r[rt];
178 r[rd] = ~(r[rs] | r[rt]);
181 r[rd] = r[rs] < r[rt] ? 1 : 0;
184 r[rd] = ((r[rs] & 0xffffffffL) < (r[rt] & 0xffffffffL)) ? 1 : 0;
187 throw new EmulationException("Illegal instruction 0/" + subcode);
195 pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
201 pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
207 pc += 4; r[RA] = pc+4; tmp = pc + branchTarget*4; nextPC = tmp;
213 pc += 4; r[RA] = pc+4; tmp = pc + branchTarget*4; nextPC = tmp;
218 throw new EmulationException("Illegal Instruction");
223 tmp = (pc&0xf0000000) | (jumpTarget << 2);
228 tmp = (pc&0xf0000000) | (jumpTarget << 2);
229 pc+=4; r[RA] = pc+4; nextPC = tmp;
234 pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
240 pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
246 pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
252 pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
257 r[rt] = r[rs] + signedImmediate;
260 r[rt] = r[rs] + signedImmediate;
263 r[rt] = r[rs] < signedImmediate ? 1 : 0;
266 r[rt] = (r[rs]&0xffffffffL) < (unsignedImmediate&0xffffffffL) ? 1 : 0;
269 r[rt] = r[rs] & unsignedImmediate;
272 r[rt] = r[rs] | unsignedImmediate;
275 r[rt] = r[rs] ^ unsignedImmediate;
278 r[rt] = unsignedImmediate << 16;
281 throw new EmulationException("TLB/Exception support not implemented");
283 boolean debug = false;
284 String line = debug ? sourceLine(pc) : "";
285 boolean debugon = debug && (line.indexOf("dtoa.c:51") >= 0 || line.indexOf("dtoa.c:52") >= 0 || line.indexOf("test.c") >= 0);
286 if(rs > 8 && debugon)
287 System.out.println(" FP Op: " + op + "/" + rs + "/" + subcode + " " + line);
288 // FIXME: This could probably be removed. I don't think gcc will ever generate code that does this
289 if(roundingMode() != 0 && rs != 6 /*CTC.1*/ && !((rs==16 || rs==17) && subcode == 36 /* CVT.W.Z */))
290 throw new EmulationException("Non-cvt.w.z operation attempted with roundingMode != round to nearest");
296 if(fs != 31) throw new EmulationException("FCR " + fs + " unavailable");
303 if(fs != 31) throw new EmulationException("FCR " + fs + " unavailable");
306 case 8: // BC1F, BC1T
307 if(((fcsr&0x800000)!=0) == (((insn>>>16)&1)!=0)) {
308 pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
315 setFloat(fd,getFloat(fs)+getFloat(ft));
318 setFloat(fd,getFloat(fs)-getFloat(ft));
321 setFloat(fd,getFloat(fs)*getFloat(ft));
324 setFloat(fd,getFloat(fs)/getFloat(ft));
327 setFloat(fd,Math.abs(getFloat(fs)));
333 setFloat(fd,-getFloat(fs)); // FIXME: just flip the sign bit
336 setDouble(fd,getFloat(fs));
339 switch(roundingMode()) {
340 case 0: f[fd] = (int)Math.floor(getFloat(fs)+0.5f); break; // Round to nearest
341 case 1: f[fd] = (int)getFloat(fs); break; // Round towards zero
342 case 2: f[fd] = (int)Math.ceil(getFloat(fs)); break; // Round towards plus infinity
343 case 3: f[fd] = (int)Math.floor(getFloat(fs)); break; // Round towards minus infinity
347 setFC(getFloat(fs) == getFloat(ft)); // FIXME: just compare the ints, be sure things are normalized
350 setFC(getFloat(fs) < getFloat(ft));
352 default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
359 setDouble(fd,getDouble(fs)+getDouble(ft));
362 if(debugon) System.out.println("f" + fd + " = f" + fs + " (" + getDouble(fs) + ") - f" + ft + " (" + getDouble(ft) + ")");
363 setDouble(fd,getDouble(fs)-getDouble(ft));
366 if(debugon) System.out.println("f" + fd + " = f" + fs + " (" + getDouble(fs) + ") * f" + ft + " (" + getDouble(ft) + ")");
367 setDouble(fd,getDouble(fs)*getDouble(ft));
368 if(debugon) System.out.println("f" + fd + " = " + getDouble(fd));
371 setDouble(fd,getDouble(fs)/getDouble(ft));
374 setDouble(fd,Math.abs(getDouble(fs)));
381 setDouble(fd,-getDouble(fs)); // FIXME: just flip the sign bit
384 setFloat(fd,(float)getDouble(fs));
387 if(debugon) System.out.println("CVT.W.D rm: " + roundingMode() + " f" + fs + ":" + getDouble(fs));
388 switch(roundingMode()) {
389 case 0: f[fd] = (int)Math.floor(getDouble(fs)+0.5); break; // Round to nearest
390 case 1: f[fd] = (int)getDouble(fs); break; // Round towards zero
391 case 2: f[fd] = (int)Math.ceil(getDouble(fs)); break; // Round towards plus infinity
392 case 3: f[fd] = (int)Math.floor(getDouble(fs)); break; // Round towards minus infinity
394 if(debugon) System.out.println("CVT.W.D: f" + fd + ":" + f[fd]);
397 setFC(getDouble(fs) == getDouble(ft)); // FIXME: just compare the ints, be sure things are normalized
400 setFC(getDouble(fs) < getDouble(ft));
403 setFC(getDouble(fs) <= getDouble(ft));
405 default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
409 case 20: { // Integer
412 setDouble(fd,(double)f[fs]);
414 default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
419 throw new EmulationException("Invalid Instruction 17/" + rs);
424 throw new EmulationException("No coprocessor installed");
426 addr = r[rs] + signedImmediate;
428 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
429 } catch(RuntimeException e) {
430 tmp = memRead(addr&~3);
433 case 0: tmp = (tmp>>>24)&0xff; break;
434 case 1: tmp = (tmp>>>16)&0xff; break;
435 case 2: tmp = (tmp>>> 8)&0xff; break;
436 case 3: tmp = (tmp>>> 0)&0xff; break;
438 if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend
443 addr = r[rs] + signedImmediate;
445 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
446 } catch(RuntimeException e) {
447 tmp = memRead(addr&~3);
450 case 0: tmp = (tmp>>>16)&0xffff; break;
451 case 2: tmp = (tmp>>> 0)&0xffff; break;
453 if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend
458 addr = r[rs] + signedImmediate;
460 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
461 } catch(RuntimeException e) {
462 tmp = memRead(addr&~3);
465 case 0: r[rt] = (r[rt]&0x00000000)|(tmp<< 0); break;
466 case 1: r[rt] = (r[rt]&0x000000ff)|(tmp<< 8); break;
467 case 2: r[rt] = (r[rt]&0x0000ffff)|(tmp<<16); break;
468 case 3: r[rt] = (r[rt]&0x00ffffff)|(tmp<<24); break;
473 addr = r[rs] + signedImmediate;
475 r[rt] = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
476 } catch(RuntimeException e) {
477 r[rt] = memRead(addr);
481 addr = r[rs] + signedImmediate;
483 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
484 } catch(RuntimeException e) {
488 case 0: r[rt] = (tmp>>>24)&0xff; break;
489 case 1: r[rt] = (tmp>>>16)&0xff; break;
490 case 2: r[rt] = (tmp>>> 8)&0xff; break;
491 case 3: r[rt] = (tmp>>> 0)&0xff; break;
496 addr = r[rs] + signedImmediate;
498 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
499 } catch(RuntimeException e) {
500 tmp = memRead(addr&~3);
503 case 0: r[rt] = (tmp>>>16)&0xffff; break;
504 case 2: r[rt] = (tmp>>> 0)&0xffff; break;
509 addr = r[rs] + signedImmediate;
511 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
512 } catch(RuntimeException e) {
513 tmp = memRead(addr&~3);
516 case 0: r[rt] = (r[rt]&0xffffff00)|(tmp>>>24); break;
517 case 1: r[rt] = (r[rt]&0xffff0000)|(tmp>>>16); break;
518 case 2: r[rt] = (r[rt]&0xff000000)|(tmp>>> 8); break;
519 case 3: r[rt] = (r[rt]&0x00000000)|(tmp>>> 0); break;
524 addr = r[rs] + signedImmediate;
526 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
527 } catch(RuntimeException e) {
528 tmp = memRead(addr&~3);
531 case 0: tmp = (tmp&0x00ffffff) | ((r[rt]&0xff)<<24); break;
532 case 1: tmp = (tmp&0xff00ffff) | ((r[rt]&0xff)<<16); break;
533 case 2: tmp = (tmp&0xffff00ff) | ((r[rt]&0xff)<< 8); break;
534 case 3: tmp = (tmp&0xffffff00) | ((r[rt]&0xff)<< 0); break;
537 writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
538 } catch(RuntimeException e) {
539 memWrite(addr&~3,tmp);
544 addr = r[rs] + signedImmediate;
546 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
547 } catch(RuntimeException e) {
548 tmp = memRead(addr&~3);
551 case 0: tmp = (tmp&0x0000ffff) | ((r[rt]&0xffff)<<16); break;
552 case 2: tmp = (tmp&0xffff0000) | ((r[rt]&0xffff)<< 0); break;
555 writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
556 } catch(RuntimeException e) {
557 memWrite(addr&~3,tmp);
562 addr = r[rs] + signedImmediate;
563 tmp = memRead(addr&~3);
565 case 0: tmp=(tmp&0x00000000)|(r[rt]>>> 0); break;
566 case 1: tmp=(tmp&0xff000000)|(r[rt]>>> 8); break;
567 case 2: tmp=(tmp&0xffff0000)|(r[rt]>>>16); break;
568 case 3: tmp=(tmp&0xffffff00)|(r[rt]>>>24); break;
571 writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
572 } catch(RuntimeException e) {
573 memWrite(addr&~3,tmp);
578 addr = r[rs] + signedImmediate;
580 writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = r[rt];
581 } catch(RuntimeException e) {
582 memWrite(addr&~3,r[rt]);
586 addr = r[rs] + signedImmediate;
587 tmp = memRead(addr&~3);
589 case 0: tmp=(tmp&0x00ffffff)|(r[rt]<<24); break;
590 case 1: tmp=(tmp&0x0000ffff)|(r[rt]<<16); break;
591 case 2: tmp=(tmp&0x000000ff)|(r[rt]<< 8); break;
592 case 3: tmp=(tmp&0x00000000)|(r[rt]<< 0); break;
594 memWrite(addr&~3,tmp);
598 f[rt] = memRead(r[rs] + signedImmediate);
601 memWrite(r[rs] + signedImmediate,f[rt]);
604 throw new EmulationException("Invalid Instruction: " + op);
609 } catch(EmulationException e) {
616 // Image loading function
617 void loadImage(Object file) throws IOException {
619 if (file instanceof String) elf = new ELF(new RandomAccessFile((String)file,"r"));
620 else if (file instanceof byte[]) elf = new ELF((byte[])file);
621 if(elf.header.type != ELF.ELFHeader.ET_EXEC)
622 throw new IOException("Binary is not an executable");
623 if(elf.header.machine != ELF.ELFHeader.EM_MIPS)
624 throw new IOException("Binary is not for the MIPS I Architecture");
625 entryPoint = elf.header.entry;
626 ELF.PHeader[] pheaders = elf.pheaders;
628 for(int i=0;i<pheaders.length;i++) {
629 ELF.PHeader ph = pheaders[i];
630 if(ph.type != ELF.PHeader.PT_LOAD) continue;
631 int memsize = ph.memsz;
632 int filesize = ph.filesz;
633 if(memsize == 0) continue;
634 if(memsize < 0) throw new IOException("pheader size too large");
636 if(addr == 0x0) throw new IOException("pheader vaddr == 0x0");
637 if(addr+memsize >= (brk<<PAGE_SHIFT)) brk = (addr+memsize+PAGE_SIZE-1) >> PAGE_SHIFT;
639 for(int j=0;j<memsize+PAGE_SIZE-1;j+=PAGE_SIZE) {
640 int page = (j+addr) >>> PAGE_SHIFT;
641 if(readPages[page] == null)
642 readPages[page] = new int[PAGE_WORDS];
643 if(ph.writable()) writePages[page] = readPages[page];
646 filesize = filesize & ~3;
647 DataInputStream dis = new DataInputStream(ph.getInputStream());
649 readPages[addr >>> PAGE_SHIFT][(addr >>> 2)&(PAGE_WORDS-1)] = dis.readInt();
652 } while(filesize > 0);
660 protected void _start(int pc) {
661 registers[SP] = INITIAL_SP;
662 registers[RA] = 0xdeadbeef;
665 public Interpreter() { }
666 public Interpreter(String image) throws IOException { loadImage(image); }
667 public Interpreter(byte[] image) throws IOException { loadImage(image); }
670 // NOTE: This probably requires a jdk > 1.1, however, it is only used for debugging
671 public String sourceLine(int pc) {
672 final String addr2line = "mips-unknown-elf-addr2line";
674 if(image==null) return null;
676 Process p = Runtime.getRuntime().exec(new String[]{addr2line,"-e",image.toString(),toHex(pc)});
677 line = new BufferedReader(new InputStreamReader(p.getInputStream())).readLine();
678 if(line == null) return null;
679 while(line.startsWith("../")) line = line.substring(3);
681 } catch(IOException e) {
686 public class DebugShutdownHook implements Runnable {
689 if(getState() == RUNNING)
690 System.err.print("\nCPU Executing " + toHex(pc) + ": " + sourceLine(pc) + "\n");
694 public static void main(String[] argv) throws Exception {
695 String image = argv[0];
696 Interpreter emu = new Interpreter();
697 emu.loadImage(image);
698 Runtime.getRuntime().addShutdownHook(new Thread(emu.new DebugShutdownHook()));
700 int addr = emu.sbrk(PAGE_SIZE);
701 for(int i=0;i<10;i++) {
702 String s = "User Info item: " + (i+1) + "\0";
703 byte[] b = s.getBytes("US-ASCII");
704 emu.copyout(b,addr,b.length);
705 emu.setUserInfo(i,addr);
709 int status = emu.run(argv);
710 System.err.println("Exit status: " + status);