1 // Copyright 2003 Brian Alliet
2 // Based on org.xwt.imp.MIPS by Adam Megacz
3 // Portions Copyright 2003 Adam Megacz
7 import org.xwt.mips.util.*;
10 public class Interpreter extends UnixRuntime {
12 private int[] registers = new int[32];
15 // Floating Point Registers
16 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
30 private ELF.Symtab symtab;
32 // Register Operations
33 private final void setFC(boolean b) { fcsr = (fcsr&~0x800000) | (b ? 0x800000 : 0x000000); }
34 private final int roundingMode() { return fcsr & 3; /* bits 0-1 */ }
35 private final double getDouble(int r) {
36 return Double.longBitsToDouble(((fpregs[r+1]&0xffffffffL) << 32) | (fpregs[r]&0xffffffffL));
38 private final void setDouble(int r, double d) {
39 long l = Double.doubleToLongBits(d);
40 fpregs[r+1] = (int)(l >>> 32); fpregs[r] = (int)l;
42 private final float getFloat(int r) { return Float.intBitsToFloat(fpregs[r]); }
43 private final void setFloat(int r, float f) { fpregs[r] = Float.floatToRawIntBits(f); }
45 protected void _execute() throws ExecutionException {
48 } catch(ExecutionException e) {
49 e.setLocation(toHex(pc) + ": " + sourceLine(pc));
55 // the return value is meaningless, its just to catch people typing "return" by accident
56 private final int runSome() throws FaultException,ExecutionException {
65 insn = readPages[pc>>>PAGE_SHIFT][(pc>>>2)&PAGE_WORDS-1];
66 } catch (RuntimeException e) {
70 int op = (insn >>> 26) & 0xff; // bits 26-31
71 int rs = (insn >>> 21) & 0x1f; // bits 21-25
72 int rt = (insn >>> 16) & 0x1f; // bits 16-20
73 int ft = (insn >>> 16) & 0x1f;
74 int rd = (insn >>> 11) & 0x1f; // bits 11-15
75 int fs = (insn >>> 11) & 0x1f;
76 int shamt = (insn >>> 6) & 0x1f; // bits 6-10
77 int fd = (insn >>> 6) & 0x1f;
78 int subcode = insn & 0x3f; // bits 0-5
80 int jumpTarget = (insn & 0x03ffffff); // bits 0-25
81 int unsignedImmediate = insn & 0xffff;
82 int signedImmediate = (insn << 16) >> 16;
83 int branchTarget = signedImmediate;
85 int tmp, addr; // temporaries
94 r[rd] = r[rt] << shamt;
97 r[rd] = r[rt] >>> shamt;
100 r[rd] = r[rt] >> shamt;
103 r[rd] = r[rt] << (r[rs]&0x1f);
106 r[rd] = r[rt] >>> (r[rs]&0x1f);
109 r[rd] = r[rt] >> (r[rs]&0x1f);
112 tmp = r[rs]; pc += 4; nextPC = tmp;
115 tmp = r[rs]; pc += 4; r[rd] = pc+4; nextPC = tmp;
119 r[V0] = syscall(r[V0],r[A0],r[A1],r[A2],r[A3]);
120 if(state != RUNNING) { this.pc = nextPC; break OUTER; }
123 throw new ExecutionException("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);
154 hi = (int)((r[rs] & 0xffffffffL) % (r[rt] & 0xffffffffL));
155 lo = (int)((r[rs] & 0xffffffffL) / (r[rt] & 0xffffffffL));
159 throw new ExecutionException("ADD (add with oveflow trap) not suported");
160 /*This must trap on overflow
161 r[rd] = r[rs] + r[rt];
164 r[rd] = r[rs] + r[rt];
167 throw new ExecutionException("SUB (sub with oveflow trap) not suported");
168 /*This must trap on overflow
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];
184 r[rd] = ~(r[rs] | r[rt]);
187 r[rd] = r[rs] < r[rt] ? 1 : 0;
190 r[rd] = ((r[rs] & 0xffffffffL) < (r[rt] & 0xffffffffL)) ? 1 : 0;
193 throw new ExecutionException("Illegal instruction 0/" + subcode);
201 pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
207 pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
213 pc += 4; r[RA] = pc+4; tmp = pc + branchTarget*4; nextPC = tmp;
219 pc += 4; r[RA] = pc+4; tmp = pc + branchTarget*4; nextPC = tmp;
224 throw new ExecutionException("Illegal Instruction");
229 tmp = (pc&0xf0000000) | (jumpTarget << 2);
234 tmp = (pc&0xf0000000) | (jumpTarget << 2);
235 pc+=4; r[RA] = pc+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;
258 pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
263 r[rt] = r[rs] + signedImmediate;
266 r[rt] = r[rs] + signedImmediate;
269 r[rt] = r[rs] < signedImmediate ? 1 : 0;
272 r[rt] = (r[rs]&0xffffffffL) < (unsignedImmediate&0xffffffffL) ? 1 : 0;
275 r[rt] = r[rs] & unsignedImmediate;
278 r[rt] = r[rs] | unsignedImmediate;
281 r[rt] = r[rs] ^ unsignedImmediate;
284 r[rt] = unsignedImmediate << 16;
287 throw new ExecutionException("TLB/Exception support not implemented");
289 boolean debug = false;
290 String line = debug ? sourceLine(pc) : "";
291 boolean debugon = debug && (line.indexOf("dtoa.c:51") >= 0 || line.indexOf("dtoa.c:52") >= 0 || line.indexOf("test.c") >= 0);
292 if(rs > 8 && debugon)
293 System.out.println(" FP Op: " + op + "/" + rs + "/" + subcode + " " + line);
294 if(roundingMode() != 0 && rs != 6 /*CTC.1*/ && !((rs==16 || rs==17) && subcode == 36 /* CVT.W.Z */))
295 throw new ExecutionException("Non-cvt.w.z operation attempted with roundingMode != round to nearest");
301 if(fs != 31) throw new ExecutionException("FCR " + fs + " unavailable");
308 if(fs != 31) throw new ExecutionException("FCR " + fs + " unavailable");
311 case 8: // BC1F, BC1T
312 if(((fcsr&0x800000)!=0) == (((insn>>>16)&1)!=0)) {
313 pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
320 setFloat(fd,getFloat(fs)+getFloat(ft));
323 setFloat(fd,getFloat(fs)-getFloat(ft));
326 setFloat(fd,getFloat(fs)*getFloat(ft));
329 setFloat(fd,getFloat(fs)/getFloat(ft));
332 setFloat(fd,Math.abs(getFloat(fs)));
338 setFloat(fd,-getFloat(fs));
341 setDouble(fd,getFloat(fs));
344 switch(roundingMode()) {
345 case 0: f[fd] = (int)Math.floor(getFloat(fs)+0.5f); break; // Round to nearest
346 case 1: f[fd] = (int)getFloat(fs); break; // Round towards zero
347 case 2: f[fd] = (int)Math.ceil(getFloat(fs)); break; // Round towards plus infinity
348 case 3: f[fd] = (int)Math.floor(getFloat(fs)); break; // Round towards minus infinity
352 setFC(getFloat(fs) == getFloat(ft));
355 setFC(getFloat(fs) < getFloat(ft));
357 default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
364 setDouble(fd,getDouble(fs)+getDouble(ft));
367 if(debugon) System.out.println("f" + fd + " = f" + fs + " (" + getDouble(fs) + ") - f" + ft + " (" + getDouble(ft) + ")");
368 setDouble(fd,getDouble(fs)-getDouble(ft));
371 if(debugon) System.out.println("f" + fd + " = f" + fs + " (" + getDouble(fs) + ") * f" + ft + " (" + getDouble(ft) + ")");
372 setDouble(fd,getDouble(fs)*getDouble(ft));
373 if(debugon) System.out.println("f" + fd + " = " + getDouble(fd));
376 setDouble(fd,getDouble(fs)/getDouble(ft));
379 setDouble(fd,Math.abs(getDouble(fs)));
386 setDouble(fd,-getDouble(fs));
389 setFloat(fd,(float)getDouble(fs));
392 if(debugon) System.out.println("CVT.W.D rm: " + roundingMode() + " f" + fs + ":" + getDouble(fs));
393 switch(roundingMode()) {
394 case 0: f[fd] = (int)Math.floor(getDouble(fs)+0.5); break; // Round to nearest
395 case 1: f[fd] = (int)getDouble(fs); break; // Round towards zero
396 case 2: f[fd] = (int)Math.ceil(getDouble(fs)); break; // Round towards plus infinity
397 case 3: f[fd] = (int)Math.floor(getDouble(fs)); break; // Round towards minus infinity
399 if(debugon) System.out.println("CVT.W.D: f" + fd + ":" + f[fd]);
402 setFC(getDouble(fs) == getDouble(ft));
405 setFC(getDouble(fs) < getDouble(ft));
408 setFC(getDouble(fs) <= getDouble(ft));
410 default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
414 case 20: { // Integer
419 default: throw new ExecutionException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
424 throw new ExecutionException("Invalid Instruction 17/" + rs);
429 throw new ExecutionException("No coprocessor installed");
431 addr = r[rs] + signedImmediate;
433 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
434 } catch(RuntimeException e) {
435 tmp = memRead(addr&~3);
438 case 0: tmp = (tmp>>>24)&0xff; break;
439 case 1: tmp = (tmp>>>16)&0xff; break;
440 case 2: tmp = (tmp>>> 8)&0xff; break;
441 case 3: tmp = (tmp>>> 0)&0xff; break;
443 if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend
448 addr = r[rs] + signedImmediate;
450 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
451 } catch(RuntimeException e) {
452 tmp = memRead(addr&~3);
455 case 0: tmp = (tmp>>>16)&0xffff; break;
456 case 2: tmp = (tmp>>> 0)&0xffff; break;
458 if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend
463 addr = r[rs] + signedImmediate;
465 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
466 } catch(RuntimeException e) {
467 tmp = memRead(addr&~3);
470 case 0: r[rt] = (r[rt]&0x00000000)|(tmp<< 0); break;
471 case 1: r[rt] = (r[rt]&0x000000ff)|(tmp<< 8); break;
472 case 2: r[rt] = (r[rt]&0x0000ffff)|(tmp<<16); break;
473 case 3: r[rt] = (r[rt]&0x00ffffff)|(tmp<<24); break;
478 addr = r[rs] + signedImmediate;
480 r[rt] = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
481 } catch(RuntimeException e) {
482 r[rt] = memRead(addr);
486 addr = r[rs] + signedImmediate;
488 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
489 } catch(RuntimeException e) {
493 case 0: r[rt] = (tmp>>>24)&0xff; break;
494 case 1: r[rt] = (tmp>>>16)&0xff; break;
495 case 2: r[rt] = (tmp>>> 8)&0xff; break;
496 case 3: r[rt] = (tmp>>> 0)&0xff; break;
501 addr = r[rs] + signedImmediate;
503 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
504 } catch(RuntimeException e) {
505 tmp = memRead(addr&~3);
508 case 0: r[rt] = (tmp>>>16)&0xffff; break;
509 case 2: r[rt] = (tmp>>> 0)&0xffff; break;
514 addr = r[rs] + signedImmediate;
516 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
517 } catch(RuntimeException e) {
518 tmp = memRead(addr&~3);
521 case 0: r[rt] = (r[rt]&0xffffff00)|(tmp>>>24); break;
522 case 1: r[rt] = (r[rt]&0xffff0000)|(tmp>>>16); break;
523 case 2: r[rt] = (r[rt]&0xff000000)|(tmp>>> 8); break;
524 case 3: r[rt] = (r[rt]&0x00000000)|(tmp>>> 0); break;
529 addr = r[rs] + signedImmediate;
531 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
532 } catch(RuntimeException e) {
533 tmp = memRead(addr&~3);
536 case 0: tmp = (tmp&0x00ffffff) | ((r[rt]&0xff)<<24); break;
537 case 1: tmp = (tmp&0xff00ffff) | ((r[rt]&0xff)<<16); break;
538 case 2: tmp = (tmp&0xffff00ff) | ((r[rt]&0xff)<< 8); break;
539 case 3: tmp = (tmp&0xffffff00) | ((r[rt]&0xff)<< 0); break;
542 writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
543 } catch(RuntimeException e) {
544 memWrite(addr&~3,tmp);
549 addr = r[rs] + signedImmediate;
551 tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)];
552 } catch(RuntimeException e) {
553 tmp = memRead(addr&~3);
556 case 0: tmp = (tmp&0x0000ffff) | ((r[rt]&0xffff)<<16); break;
557 case 2: tmp = (tmp&0xffff0000) | ((r[rt]&0xffff)<< 0); break;
560 writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
561 } catch(RuntimeException e) {
562 memWrite(addr&~3,tmp);
567 addr = r[rs] + signedImmediate;
568 tmp = memRead(addr&~3);
570 case 0: tmp=(tmp&0x00000000)|(r[rt]>>> 0); break;
571 case 1: tmp=(tmp&0xff000000)|(r[rt]>>> 8); break;
572 case 2: tmp=(tmp&0xffff0000)|(r[rt]>>>16); break;
573 case 3: tmp=(tmp&0xffffff00)|(r[rt]>>>24); break;
576 writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = tmp;
577 } catch(RuntimeException e) {
578 memWrite(addr&~3,tmp);
583 addr = r[rs] + signedImmediate;
585 writePages[addr>>>PAGE_SHIFT][(addr>>>2)&(PAGE_WORDS-1)] = r[rt];
586 } catch(RuntimeException e) {
587 memWrite(addr&~3,r[rt]);
591 addr = r[rs] + signedImmediate;
592 tmp = memRead(addr&~3);
594 case 0: tmp=(tmp&0x00ffffff)|(r[rt]<<24); break;
595 case 1: tmp=(tmp&0x0000ffff)|(r[rt]<<16); break;
596 case 2: tmp=(tmp&0x000000ff)|(r[rt]<< 8); break;
597 case 3: tmp=(tmp&0x00000000)|(r[rt]<< 0); break;
599 memWrite(addr&~3,tmp);
602 // FEATURE: Needs to be atomic w/ threads
604 r[rt] = memRead(r[rs] + signedImmediate);
607 f[rt] = memRead(r[rs] + signedImmediate);
609 // FEATURE: Needs to be atomic w/ threads
611 memWrite(r[rs] + signedImmediate,r[rt]);
615 memWrite(r[rs] + signedImmediate,f[rt]);
618 throw new ExecutionException("Invalid Instruction: " + op);
623 } catch(ExecutionException e) {
630 public int lookupSymbol(String name) {
631 ELF.Symbol sym = symtab.getGlobalSymbol(name);
632 return sym == null ? -1 : sym.addr;
635 // Image loading function
636 private void loadImage(SeekableData data) throws IOException {
637 if(state != UNINITIALIZED) throw new IllegalStateException("loadImage called on initialized runtime");
639 ELF elf = new ELF(data);
640 symtab = elf.getSymtab();
642 if(elf.header.type != ELF.ELFHeader.ET_EXEC) throw new IOException("Binary is not an executable");
643 if(elf.header.machine != ELF.ELFHeader.EM_MIPS) throw new IOException("Binary is not for the MIPS I Architecture");
644 if(elf.ident.data != ELF.ELFIdent.ELFDATA2MSB) throw new IOException("Binary is not big endian");
646 entryPoint = elf.header.entry;
648 ELF.Symtab symtab = elf.getSymtab();
649 if(symtab == null) throw new IOException("No symtab in binary (did you strip it?)");
650 ELF.Symbol userInfo = symtab.getGlobalSymbol("user_info");
651 ELF.Symbol gpsym = symtab.getGlobalSymbol("_gp");
653 if(gpsym == null) throw new IOException("NO _gp symbol!");
656 if(userInfo != null) {
657 userInfoBase = userInfo.addr;
658 userInfoSize = userInfo.size;
661 ELF.PHeader[] pheaders = elf.pheaders;
663 for(int i=0;i<pheaders.length;i++) {
664 ELF.PHeader ph = pheaders[i];
665 if(ph.type != ELF.PHeader.PT_LOAD) continue;
666 int memsize = ph.memsz;
667 int filesize = ph.filesz;
668 if(memsize == 0) continue;
669 if(memsize < 0) throw new IOException("pheader size too large");
671 if(addr == 0x0) throw new IOException("pheader vaddr == 0x0");
672 brk = max(addr+memsize,brk);
674 for(int j=0;j<memsize+PAGE_SIZE-1;j+=PAGE_SIZE) {
675 int page = (j+addr) >>> PAGE_SHIFT;
676 if(readPages[page] == null)
677 readPages[page] = new int[PAGE_WORDS];
678 if(ph.writable()) writePages[page] = readPages[page];
681 filesize = filesize & ~3;
682 DataInputStream dis = new DataInputStream(ph.getInputStream());
684 readPages[addr >>> PAGE_SHIFT][(addr >>> 2)&(PAGE_WORDS-1)] = dis.readInt();
687 } while(filesize > 0);
691 brkAddr = (brk+PAGE_SIZE-1)&~(PAGE_SIZE-1);
695 protected void setCPUState(CPUState state) {
696 for(int i=1;i<32;i++) registers[i] = state.r[i];
697 for(int i=0;i<32;i++) fpregs[i] = state.f[i];
698 hi=state.hi; lo=state.lo; fcsr=state.fcsr;
702 protected CPUState getCPUState() {
703 CPUState state = new CPUState();
704 for(int i=1;i<32;i++) state.r[i] = registers[i];
705 for(int i=0;i<32;i++) state.f[i] = fpregs[i];
706 state.hi=hi; state.lo=lo; state.fcsr=fcsr;
711 // This is package private for fork() which does all kinds of ugly things behind the scenes
712 Interpreter() { super(4096,65536,true); }
713 public Interpreter(SeekableData data) throws IOException { this(); loadImage(data); }
714 public Interpreter(String filename) throws IOException {
715 this(new SeekableFile(filename,false));
718 public Interpreter(InputStream is) throws IOException { this(new SeekableInputStream(is)); }
721 // NOTE: This probably requires a jdk > 1.1, however, it is only used for debugging
722 private java.util.HashMap sourceLineCache;
723 public String sourceLine(int pc) {
724 final String addr2line = "mips-unknown-elf-addr2line";
725 String line = (String) (sourceLineCache == null ? null : sourceLineCache.get(new Integer(pc)));
726 if(line != null) return line;
727 if(image==null) return null;
729 Process p = java.lang.Runtime.getRuntime().exec(new String[]{addr2line,"-e",image,toHex(pc)});
730 line = new BufferedReader(new InputStreamReader(p.getInputStream())).readLine();
731 if(line == null) return null;
732 while(line.startsWith("../")) line = line.substring(3);
733 if(sourceLineCache == null) sourceLineCache = new java.util.HashMap();
734 sourceLineCache.put(new Integer(pc),line);
736 } catch(IOException e) {
741 public class DebugShutdownHook implements Runnable {
743 int pc = Interpreter.this.pc;
744 if(getState() == RUNNING)
745 System.err.print("\nCPU Executing " + toHex(pc) + ": " + sourceLine(pc) + "\n");
749 public static void main(String[] argv) throws Exception {
750 String image = argv[0];
751 Interpreter emu = new Interpreter(image);
752 java.lang.Runtime.getRuntime().addShutdownHook(new Thread(emu.new DebugShutdownHook()));
753 int status = emu.run(argv);
754 System.err.println("Exit status: " + status);