--- /dev/null
+// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.xwt.imp;
+
+import java.util.*;
+import java.io.*;
+
+// FIXME: lb/sb/sh/lh need not be word aligned
+// FIXME: memory accesses aren't handling sign-extending properly
+// FIXME: probably have to implement nonaligned access
+// FIXME: implement malloc()
+
+// FIXME: implement an ELF parser based on RandomAccessFile
+
+// FEATURE: progress indicator
+// FEATURE: support n32 abi (passes more arguments in registers)
+// FEATURE: trap on arithmetic overflows
+// FEATURE: FPU
+// FEATURE: we always know the value of the pc register; we should emit it as a literal when it appears in computations
+// FEATURE: emit bytecode rather than .java code (for on-the-fly classloading without javac present in "real" JVMs)
+
+/** reads a fully linked MIPS ELF binary image on stdin; writes a .java file on stdout */
+public class MIPS {
+
+ static String runs = "";
+ static int last_emit = -1;
+ static DataInputStream dis;
+ public static void main(String[] s) throws IOException {
+
+ if (s.length != 2) {
+ System.err.println("usage: java " + MIPS.class.getName() + " <classname> <binary.mips>");
+ System.exit(-1);
+ }
+
+ String packageName = null;
+ String className = s[0];
+ if (s[0].indexOf('.') != -1) {
+ packageName = s[0].substring(0, s[0].lastIndexOf('.'));
+ className = s[0].substring(s[0].lastIndexOf('.') + 1);
+ }
+
+ System.out.println(prefix + "// This file was generated by MipsToJava");
+ if (packageName != null) System.out.println(prefix + "package " + packageName + ";");
+ System.out.println(prefix + "public class " + className + " {");
+ System.out.println(prefix + "");
+ System.out.println(prefix + " public " + className + "() { }");
+ System.out.println(prefix + "");
+ System.out.println(prefix + " // memory");
+ System.out.println(prefix + " public int mem_read[][] = new int[65535][];");
+ System.out.println(prefix + "");
+ System.out.println(prefix + " // same as mem_read unless a page is write-protected");
+ System.out.println(prefix + " public int mem_write[][] = new int[65535][];");
+ System.out.println(prefix + "");
+ System.out.println(prefix + " // program counter");
+ System.out.println(prefix + " int pc = 0;");
+ System.out.println(prefix + "");
+ System.out.println(prefix + " // temporary");
+ System.out.println(prefix + " int tmp = 0;");
+ System.out.println(prefix + "");
+ System.out.println(prefix + " // MIPS multiply/divide subsystem; 64-bit result");
+ System.out.println(prefix + " long hilo = 0;");
+ System.out.println(prefix + "");
+ System.out.println(prefix + " // General Purpose registers");
+ System.out.println(prefix + " final int r0 = 0;");
+ System.out.println(prefix + " int r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0,");
+ System.out.println(prefix + " r8 = 0, r9 = 0, r10 = 0, r11 = 0, r12 = 0, r13 = 0, r14 = 0, r15 = 0,");
+ System.out.println(prefix + " r16 = 0, r17 = 0, r18 = 0, r19 = 0, r20 = 0, r21 = 0, r22 = 0, r23 = 0,");
+ System.out.println(prefix + " r24 = 0, r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0, r30 = 0, r31 = 0;");
+ System.out.println(prefix + "");
+
+ dis = new DataInputStream(new FileInputStream(s[1]));
+
+ // read the ELF header
+ if (dis.readByte() != 0x7f || dis.readByte() != 'E' || dis.readByte() != 'L' || dis.readByte() != 'F')
+ throw new RuntimeException("input file is not an ELF binary");
+ dis.skip(12);
+
+ if (dis.readShort() != 2)
+ throw new RuntimeException("binary is not a linked executable");
+
+ if (dis.readShort() != 8)
+ throw new RuntimeException("binary is not a MIPS R3000 binary");
+
+ dis.skip(4);
+
+ int entry_point = dis.readInt();
+ String entry_point_string = Long.toString(entry_point & 0xffffffffL, 16);
+
+ int ph_offset = dis.readInt();
+ int sh_offset = dis.readInt();
+ if (ph_offset == 0) throw new RuntimeException("binary is not statically linked");
+ dis.skip(4);
+ dis.skip(2);
+
+ int ph_entry_size = dis.readShort();
+ int ph_num_entries = dis.readShort();
+ int sh_entry_size = dis.readShort();
+ int sh_num_entries = dis.readShort();
+ int string_table_section_number = dis.readShort();
+
+ int skipamount = sh_offset - (4 + 12 + 2 + 2 + 4 + 4 + 4 + 4 + 4 + 2 + 2 + 2 + 2 + 2 + 2);
+ while (skipamount>0) skipamount -= (int)dis.skip(skipamount);
+
+ int[] p_type = new int[sh_num_entries];
+ int[] p_ofs = new int[sh_num_entries];
+ int[] addr = new int[sh_num_entries];
+ int[] p_size = new int[sh_num_entries];
+ int[] p_name = new int[sh_num_entries];
+ for(int i=0; i<sh_num_entries; i++) {
+ p_name[i] = dis.readInt();
+ p_type[i] = dis.readInt();
+ dis.skip(4);
+ addr[i] = dis.readInt();
+ p_ofs[i] = dis.readInt();
+ p_size[i] = dis.readInt();
+ dis.skip(sh_entry_size - 4 * 6);
+ }
+
+ dis.close();
+ dis = new DataInputStream(new FileInputStream(s[1]));
+
+ int seek = p_ofs[string_table_section_number];
+ while (seek > 0) seek -= dis.skip(seek);
+ char[] stringTable = new char[p_size[string_table_section_number]];
+ for(int i=0; i<p_size[string_table_section_number]; i++)
+ stringTable[i] = (char)dis.readByte();
+
+ dis.close();
+ dis = new DataInputStream(new FileInputStream(s[1]));
+
+ int pos = 0;
+ for(int i=0; i<sh_num_entries; i++) {
+
+ String name = "";
+ for(int j=p_name[i]; j<stringTable.length && stringTable[j] != 0; j++) name += stringTable[j];
+ System.out.println();
+ System.out.println(prefix + "// section \"" + name +
+ "\" #" + i + "; file offset 0x" + Long.toString(p_ofs[i] & 0xffffffffL, 16) +
+ ", vma 0x" + Long.toString(addr[i] & 0xffffffffL, 16) +
+ ", size 0x" + Long.toString(p_size[i] & 0xffffffff, 16));
+
+ if (name.equals(".sdata")) {
+ if (last_emit != -1) {
+ System.out.println(prefix + " case 0x" + Long.toString((last_emit & 0xffffffffL) + 0x1000, 16) + ": return;");
+ System.out.println(prefix + " default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
+ System.out.println(prefix + " }");
+ System.out.println(prefix + " }");
+ last_emit = -1;
+ }
+ pos = 0; dis.close(); dis = new DataInputStream(new FileInputStream(s[1]));
+ while(pos < p_ofs[i]) pos += dis.skip(p_ofs[i] - pos);
+ String base = "0x" + Long.toString((addr[i] & 0xffff0000L) >> 16, 16);
+ System.out.println(prefix + " private void initData() {");
+ System.out.println(prefix + " r28 = 0x" + Long.toString((addr[i] - Short.MIN_VALUE - 12) & 0xffffffffL, 16) + ";");
+ System.out.println(prefix + " mem_read[" + base + "] = mem_write[" + base + "] = new int[65535];");
+ for(long k=addr[i] & 0xffffffffL; k<((addr[i] + p_size[i]) & 0xffffffffL); k += 4)
+ System.out.println(prefix + " mem_write[" + base + "][0x" + Long.toString(k & 0xffff, 16) + "] = 0x" +
+ Long.toString(dis.readInt() & 0xffffffffL, 16) + ";");
+ System.out.println(prefix + " }");
+
+ } else if (name.equals(".text") /*|| name.equals(".init")*/) {
+ if (pos > p_ofs[i]) { pos = 0; dis.close(); dis = new DataInputStream(new FileInputStream(s[1])); }
+ while(pos < p_ofs[i]) pos += dis.skip(p_ofs[i] - pos);
+ int remaining = p_size[i];
+ for(int ofs = addr[i]; ofs < addr[i] + p_size[i];) {
+ String base = Long.toString(ofs & 0xffffff00L, 16);
+ int len = Math.min(((ofs + 0x100) & 0xffffff00) - ofs, remaining);
+ emit(ofs, len, dis);
+ last_emit = ofs;
+ remaining -= len;
+ ofs += len;
+ }
+ }
+ }
+
+ if (last_emit != -1) {
+ System.out.println(prefix + " case 0x" + Long.toString((last_emit & 0xffffffffL) + 0x1000, 16) + ": return;");
+ System.out.println(prefix + " default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
+ System.out.println(prefix + " }");
+ System.out.println(prefix + " }");
+ last_emit = -1;
+ }
+
+ System.out.println();
+ System.out.println(prefix + " public static void main(String[] s) { new " + className + "().main(); }");
+ System.out.println();
+ System.out.println(prefix + " public void main() {");
+ System.out.println();
+ System.out.println(prefix + " // allocate the stack");
+ System.out.println(prefix + " mem_read[1] = mem_write[1] = new int[65535];");
+ System.out.println();
+ System.out.println(prefix + " // set the stack pointer");
+ System.out.println(prefix + " r29 = 0x0001ff00;");
+ System.out.println();
+ System.out.println(prefix + " // set the \"return address\" from _start to point at the \"magic exit address\" (0xdeadbeef)");
+ System.out.println(prefix + " r31 = 0xdeadbeef;");
+ System.out.println();
+ System.out.println(prefix + " // read in the .data segment");
+ System.out.println(prefix + " initData();");
+ System.out.println();
+ System.out.println(prefix + " trampoline(0x" + entry_point_string + ");");
+ System.out.println();
+ System.out.println(prefix + " }");
+
+ System.out.println();
+ System.out.println(prefix + " public void trampoline(int pc) {");
+ System.out.println(prefix + " this.pc = pc;");
+ System.out.println(prefix + " while(true) {");
+ System.out.println(prefix + " switch(this.pc & 0xffffff00) {");
+ System.out.println(prefix + " case 0xdeadbe00: System.out.println(\"exiting with return value \" + r2); System.exit(r2); continue;");
+ System.out.print(runs);
+ System.out.println(prefix + " default: throw new Error(\"invalid address 0x\" + Long.toString(this.pc&0xffffffffL,16));");
+ System.out.println(prefix + " }");
+ System.out.println(prefix + " }");
+ System.out.println(prefix + " }");
+ System.out.println(prefix + "}");
+ }
+
+ static int _instruction;
+ static boolean readnext = true;
+
+ /** reads <tt>numbytes</tt> from the stream, emitting <tt>case</tt> blocks starting at vaddr <tt>ofs</tt> */
+ static void emit(int vaddr, int numbytes, DataInputStream dis) throws IOException {
+ if (last_emit != -1 && ((last_emit & 0xffffff00) != (vaddr & 0xffffff00))) {
+ System.out.println(prefix + " case 0x" + Long.toString((last_emit & 0xffffffffL) + 0x1000, 16) + ": return;");
+ System.out.println(prefix + " default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
+ System.out.println(prefix + " }");
+ System.out.println(prefix + " }");
+ }
+ if (last_emit == -1 || ((last_emit & 0xffffff00) != (vaddr & 0xffffff00))) {
+ System.out.println("");
+ System.out.println(prefix + " private void run_" + Long.toString(vaddr & 0xffffff00L,16) + "() {");
+ runs += " case 0x" + Long.toString(vaddr & 0xffffff00L,16) +
+ ": run_" + Long.toString(vaddr & 0xffffff00L,16) + "(); continue;\n";
+ System.out.println(prefix + " switch(pc) {");
+ }
+ int ofs = vaddr;
+ try {
+ OUTER: for(ofs = vaddr; ofs < vaddr + numbytes; ofs+=4) {
+ if (readnext) _instruction = dis.readInt();
+ readnext = true;
+
+ String istring = Long.toString(_instruction & 0xffffffffL, 16);
+ while(istring.length() < 8) istring = "0" + istring;
+ String ostring = Long.toString(ofs & 0xffffffffL, 16);
+ while(ostring.length() < 8) ostring = "0" + ostring;
+ System.out.print(prefix + " /* " + istring + " */ case 0x" + ostring + ": System.out.println(\"pc=0x\" + Long.toString(pc&0xffffffffL,16));");
+
+ emit_instruction(ofs, _instruction);
+ }
+ } catch (EOFException e) {
+ emit(ofs, " // warning, reached EOF before section end");
+ } finally {
+ last_emit = ofs;
+ }
+ }
+
+ private static void emit_instruction(int ofs, int instruction) throws IOException {
+ int op = (instruction >>> 26) & 0xff; // bits 26-31
+ int rs = (instruction >> 21) & 0x1f; // bits 21-25
+ int rt = (instruction >> 16) & 0x1f; // bits 16-20
+ int rd = (instruction >> 11) & 0x1f; // bits 11-15
+ int shamt = (instruction >> 6) & 0x1f; // bits 6-10
+ int subcode = instruction & 0x3f; // bits 0-5
+
+ int branch_offset = (((instruction & 0x8000) << 16) | ((instruction & 0x7fff))) * 4;
+ int jump_target = (instruction & 0x03ffffff) * 4;
+ int signed_immediate = (int)((short)(instruction & 0xffff));
+ int unsigned_immediate = instruction & 0xffff;
+
+ switch(op) {
+
+ case 0: {
+ switch(subcode) {
+
+ case 0:
+ if (instruction == 0) emit(ofs, " /* NOP */;"); // NOP
+ else emit(ofs, " r" + rd + " = r" + rt + " << " + shamt + ";"); // SLL
+ break;
+
+ case 1: throw new Error("opcode 0, subcode 1 is not part of the MIPS I instruction set");
+
+ case 2:
+ emit(ofs, " r" + rd + " = r" + rt + " >>> " + shamt + ";"); // SRL
+ break;
+
+ case 3:
+ emit(ofs, " r" + rd + " = r" + rt + " >> " + shamt + ";"); // SRA
+ break;
+
+ case 4:
+ emit(ofs, " r" + rd + " = r" + rt + " << (r" + rs + " % 32);"); // SLLV
+ break;
+
+ case 5: throw new Error("opcode 0, subcode 5 is not part of the MIPS I instruction set");
+
+ case 6:
+ emit(ofs, " r" + rd + " = r" + rs + " >>> (r" + rt + " % 32);"); // SRLV
+ break;
+
+ case 7:
+ emit(ofs, " r" + rd + " = r" + rs + " >> (r" + rt + " % 32);"); // SRAV
+ break;
+
+ case 8:
+ _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
+ emit(ofs, " pc = r" + rs + "; return;"); // JR
+ break;
+
+ case 9:
+ _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
+ emit(ofs, " r" + rd + " = pc + 8; pc = r" + rs + "; return;"); // JALR
+ break;
+
+ case 10: throw new Error("opcode 0, subcode 10 is not part of the MIPS I instruction set");
+ case 11: throw new Error("opcode 0, subcode 11 is not part of the MIPS I instruction set");
+
+ case 12:
+ emit(ofs, " syscall(" + ((instruction & 0x07ffffc0) >> 6) + ");"); // SYSCALL
+ break;
+
+ case 13:
+ emit(ofs, " /* BREAKPOINT */");
+ break;
+
+ case 14: throw new Error("opcode 0, subcode 14 is not part of the MIPS I instruction set");
+ case 15: throw new Error("opcode 0, subcode 15 is not part of the MIPS I instruction set");
+
+ case 16:
+ emit(ofs, " r" + rd + " = (int)(hilo >>> 32);"); // MFHI
+ break;
+
+ case 17:
+ emit(ofs, " hilo = (hilo & 0x00000000ffffffffL) | ((r" + rs + " & 0xffffffffL) << 32);"); // MTHI
+ break;
+
+ case 18:
+ emit(ofs, " r" + rd + " = (int)hilo;"); // MFLO
+ break;
+
+ case 19:
+ emit(ofs, " hilo = (hilo & 0xffffffff00000000L) | (r" + rs + " & 0xffffffffL);"); // MTLO
+ break;
+
+ case 20: throw new Error("opcode 0, subcode 20 is not part of the MIPS I instruction set");
+ case 22: throw new Error("opcode 0, subcode 22 is not part of the MIPS I instruction set");
+ case 23: throw new Error("opcode 0, subcode 23 is not part of the MIPS I instruction set");
+
+ case 24:
+ emit(ofs, " hilo = ((long)r" + rs + ") * ((long)r" + rt + ");"); // MULT
+ break;
+
+ case 25:
+ emit(ofs, " hilo = (r" + rs + " & 0xffffffffL) * (r" + rt + " & 0xffffffffL);"); // MULTU
+ break;
+
+ case 26:
+ emit(ofs, " hilo = (((r" + rs + " % r" + rt +") & 0xffffffffL) << 32) | ((r" + rs + " / r" + rt +") & 0xffffffffL);"); // DIV
+ break;
+
+ case 27:
+ emit(ofs, " hilo = (((r" + rs + " & 0xffffffffL) % (r" + rt +" & 0xffffffffL)) << 32) | " + // DIVU
+ "((r" + rs + " & 0xffffffffL) / (r" + rt +" & 0xffffffffL));");
+ break;
+
+ case 28: throw new Error("opcode 0, subcode 28 is not part of the MIPS I instruction set");
+ case 29: throw new Error("opcode 0, subcode 29 is not part of the MIPS I instruction set");
+ case 30: throw new Error("opcode 0, subcode 30 is not part of the MIPS I instruction set");
+ case 31: throw new Error("opcode 0, subcode 31 is not part of the MIPS I instruction set");
+
+ case 32:
+ emit(ofs, " r" + rd + " = r" + rs + " + r" + rt + ";"); // ADD
+ break;
+
+ case 33:
+ emit(ofs, " r" + rd + " = (int)(((r" + rs + " & 0xffffffffL) + (r" + rt + " & 0xffffffffL)));"); // ADDU
+ break;
+
+ case 34:
+ emit(ofs, " r" + rd + " = r" + rs + " - r" + rt + ";"); // SUB
+ break;
+
+ case 35:
+ emit(ofs, " r" + rd + " = (int)(((r" + rs + " & 0xffffffffL) - (r" + rt + " & 0xffffffffL)));"); // SUBU
+ break;
+
+ case 36:
+ emit(ofs, " r" + rd + " = r" + rs + " & r" + rt + ";"); // AND
+ break;
+
+ case 37:
+ emit(ofs, " r" + rd + " = r" + rs + " | r" + rt + ";"); // OR
+ break;
+
+ case 38:
+ emit(ofs, " r" + rd + " = r" + rs + " ^ r" + rt + ";"); // XOR
+ break;
+
+ case 39:
+ emit(ofs, " r" + rd + " = ~(r" + rs + " | r" + rt + ");"); // NOR
+ break;
+
+ case 40: throw new Error("opcode 0, subcode 40 is not part of the MIPS I instruction set");
+ case 41: throw new Error("opcode 0, subcode 41 is not part of the MIPS I instruction set");
+
+ case 42:
+ emit(ofs, " r" + rd + " = (r" + rs + " < r" + rt + ") ? 1 : 0;"); // SLT
+ break;
+
+ case 43:
+ emit(ofs, " r" + rd + " = ((r" + rs + " & 0xffffffffL) < (r" + rt + " & 0xffffffffL)) ? 1 : 0;"); // SLTU
+ break;
+
+ case 44: case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: case 53: case 54:
+ case 55: case 56: case 57: case 58: case 59: case 60: case 61: case 62: case 63:
+ throw new Error("opcode 0, subcode " + subcode + " is not part of the MIPS I instruction set");
+ default:
+ throw new Error("opcode 0, subcode " + subcode + " is not a valid MIPS instruction");
+ }
+ break;
+ }
+
+ case 1: {
+ switch(rt) {
+ case 0:
+ _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
+ emit(ofs, " if (r" + rs + " < 0) { pc += " + (branch_offset + 4) + "; return; }; "); // BLTZ
+ break;
+
+ case 1:
+ _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
+ emit(ofs, " if (r" + rs + " >= 0) { pc += " + (branch_offset + 4) + "; return; }; "); // BGEZ
+ break;
+
+ case 16:
+ _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
+ emit(ofs, " r31 = pc + 4; if (r" + rs + " < 0) { pc += " + (branch_offset + 4) + "; return; }; "); // BLTZAL
+ break;
+
+ case 17:
+ _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
+ emit(ofs, " r31 = pc + 4; if (r" + rs + " >= 0) { pc += " + (branch_offset + 4) + "; return; }; "); // BGEZAL
+ break;
+
+ default: throw new Error("opcode 1, subcode " + rt + " is not part of the MIPS I instruction set");
+ }
+ break;
+ }
+
+ case 2:
+ _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
+ emit(ofs, " pc &= 0xf0000000; pc |= 0x" + Long.toString(jump_target & 0xffffffffL, 16) + "; return;"); // J
+ break;
+
+ case 3:
+ _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
+ emit(ofs, " r31 = pc + 4; pc &= 0xf0000000; pc |= 0x" + Long.toString(jump_target & 0xffffffffL, 16) + "; return;"); // JAL
+ break;
+
+ case 4:
+ _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
+ emit(ofs, " if (r" + rs + " == r" + rt + ") { pc += 0x" + Long.toString((branch_offset + 4) & 0xffffffffL, 16) + "; return; }; "); // BEQ
+ break;
+
+ case 5:
+ _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
+ emit(ofs, " if (r" + rs + " != r" + rt + ") { pc += 0x" + Long.toString((branch_offset + 4) & 0xffffffffL, 16) + "; return; }; "); // BNE
+ break;
+
+ case 6:
+ _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
+ emit(ofs, " if (r" + rs + " <= 0) { pc += 0x" + Long.toString((branch_offset + 4) & 0xffffffffL, 16) + "; return; }; "); // BLEZ
+ break;
+
+ case 7:
+ _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
+ emit(ofs, " if (r" + rs + " > 0) { pc += 0x" + Long.toString((branch_offset + 4) & 0xffffffffL, 16) + "; return; }; "); // BGTZ
+ break;
+
+ case 8: case 9:
+ emit(ofs, " r" + rt + " = r" + rs + " + " + signed_immediate + ";"); // ADDI[U]
+ break;
+
+ case 10:
+ emit(ofs, " r" + rt + " = (r" + rs + " < " + signed_immediate + ") ? 1 : 0;"); // SLTI
+ break;
+
+ case 11:
+ emit(ofs, " r" + rt + " = ((r" + rs + " & 0xffffffffL) < (" + signed_immediate + " & 0xffffffffL)) ? 1 : 0;"); // SLTIU
+ break;
+
+ case 12:
+ emit(ofs, " r" + rt + " = r" + rs + " & " + unsigned_immediate + ";"); // ANDI
+ break;
+
+ case 13:
+ emit(ofs, " r" + rt + " = r" + rs + " | " + unsigned_immediate + ";"); // ORI
+ break;
+
+ case 14:
+ emit(ofs, " r" + rt + " = r" + rs + " ^ " + unsigned_immediate + ";"); // XORI
+ break;
+
+ case 15:
+ emit(ofs, " r" + rt + " = " + unsigned_immediate + " << 16;"); // LUI
+ break;
+
+ case 16: /* throw new Error("coprocessor instructions (opcode 16) are not implemented"); */
+ emit(ofs, " throw new Error(\"coprocessor instructions (opcode 16) are not implemented\");");
+ break;
+ case 17: throw new Error("coprocessor instructions (opcode 17) are not implemented");
+ case 18: throw new Error("coprocessor instructions (opcode 18) are not implemented");
+
+ case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28:
+ throw new Error("opcode " + op + " is not part of the MIPS I instruction set @" + Long.toString(ofs & 0xffffffffL, 16));
+
+ case 32: {
+ String dest = "(r" + rs + " + " + signed_immediate + ")";
+ emit(ofs, " tmp = mem_read[" + dest + ">>>16][" + dest + " & 0xffff]; r" + rt + " = ((tmp & 0x80) << 24) | (tmp & 0x7f);"); // LB
+ break;
+ }
+
+ case 33: {
+ String dest = "(r" + rs + " + " + signed_immediate + ")";
+ emit(ofs, " tmp = mem_read[" + dest + ">>>16][" + dest + " & 0xffff]; r" + rt + " = ((tmp & 0x8000) << 16) | (tmp & 0x7fff);"); // LH
+ break;
+ }
+
+ case 34:
+ emit(ofs, " throw new Error(\"LWL (opcode 34) is not supported; are you sure you used -mstrict-align?\");");
+ break;
+
+ case 35: {
+ String dest = "(r" + rs + " + " + signed_immediate + ")";
+ emit(ofs, " r" + rt + " = mem_read[" + dest + ">>>16][" + dest + " & 0xffff];"); // LW
+ break;
+ }
+
+ case 36: {
+ String dest = "(r" + rs + " + " + signed_immediate + ")";
+ emit(ofs, " r" + rt + " = mem_read[" + dest + ">>>16][" + dest + " & 0xffff] & 0xff;"); // LBU
+ break;
+ }
+
+ case 37: {
+ String dest = "(r" + rs + " + " + signed_immediate + ")";
+ emit(ofs, " r" + rt + " = mem_read[" + dest + ">>>16][" + dest + " & 0xffff] & 0xffff;"); // LHU
+ break;
+ }
+
+ case 38:
+ emit(ofs, " throw new Error(\"LWR (opcode 38) is not supported; are you sure you used -mstrict-align?\");");
+ break;
+
+ case 39: throw new Error("opcode 39 is not part of the MIPS I instruction set");
+
+ case 40: {
+ String dest = "(r" + rs + " + " + signed_immediate + ")";
+ emit(ofs, " tmp = mem_read[" + dest + ">>>16][" + dest + " & 0xffff]; " +
+ "mem_write[" + dest + ">>>16][" + dest + " & 0xffff] = (tmp & 0xffffff00) | (r" + rt + " & 0xff);"); // SB
+ break;
+ }
+
+ case 41: {
+ String dest = "(r" + rs + " + " + signed_immediate + ")";
+ emit(ofs, " tmp = mem_read[" + dest + ">>>16][" + dest + " & 0xffff]; " +
+ "mem_write[" + dest + ">>>16][" + dest + " & 0xffff] = (tmp & 0xffff0000) | (r" + rt + " & 0xffff);"); // SH
+ break;
+ }
+
+ case 42:
+ emit(ofs, " throw new Error(\"SWL (opcode 42) is not supported; are you sure you used -mstrict-align?\");");
+ break;
+
+ case 43: {
+ String dest = "(r" + rs + " + " + signed_immediate + ")";
+ emit(ofs, " mem_write[" + dest + ">>>16][" + dest + " & 0xffff] = r" + rt + ";"); // SW
+ break;
+ }
+
+ case 44: throw new Error("opcode 44 is not part of the MIPS I instruction set");
+ case 45: throw new Error("opcode 45 is not part of the MIPS I instruction set");
+
+ case 46:
+ emit(ofs, " throw new Error(\"SWR (opcode 46) is not supported; are you sure you used -mstrict-align?\");");
+ break;
+
+ case 47: throw new Error("opcode 47 is not part of the MIPS I instruction set");
+ case 48: throw new Error("opcode 48 is not part of the MIPS I instruction set");
+
+ case 49:
+ emit(ofs, " throw new Error(\"floating point operations (opcode 49) are not yet supported\");");
+ break;
+
+ case 50:
+ emit(ofs, " throw new Error(\"floating point operations (opcode 50) are not yet supported\");");
+ break;
+
+ case 51: case 52: case 53: case 54: case 55: case 56:
+ throw new Error("opcode " + op + " is not part of the MIPS I instruction set");
+
+ case 57:
+ emit(ofs, " throw new Error(\"floating point operations (opcode 57) are not yet supported\");");
+ break;
+
+ case 58:
+ emit(ofs, " throw new Error(\"floating point operations (opcode 58) are not yet supported\");");
+ break;
+
+ case 60: case 61: case 62: case 63:
+ throw new Error("opcode " + op + " is not part of the MIPS I instruction set");
+
+ default:
+ throw new Error("unknown opcode " + op);
+ }
+ }
+
+
+
+ static String prefix = "";
+ static void emit(int vaddr, String s) {
+ if (s.indexOf("r0 = ") != -1) s = " /* NOP */";
+ if (!s.trim().endsWith("return;") && s.indexOf("throw") == -1) s += " pc = 0x" + Long.toString((vaddr + 4) & 0xffffffffL,16) + ";";
+ System.out.println(s);
+ }
+}
+
--- /dev/null
+// Copyright 2003 Adam Megacz
+// Author Brian Alliet
+// Based on org.xwt.imp.MIPS by Adam Megacz
+
+package org.xwt.imp;
+
+import java.io.*;
+
+public class MIPSInterpreter extends MIPSEmu {
+
+ // Registers
+ private int[] registers = new int[32];
+ private int hi,lo;
+
+ // Floating Point Registers
+ private int[] fpregs = new int[32];
+
+ // 24-31 - unused
+ // 23 - conditional bit
+ // 18-22 - unused
+ // 12-17 - cause bits (unimplemented)
+ // 7-11 - enables bits (unimplemented)
+ // 2-6 - flags (unimplemented)
+ // 0-1 - rounding mode (only implemented for fixed point conversions)
+ private int fcsr;
+
+ private int nextPC;
+
+ // The filename if the binary we're running
+ private String image;
+
+ // Register Operations
+ private final void setFC(boolean b) { fcsr = (fcsr&~0x800000) | (b ? 0x800000 : 0x000000); }
+ private final int roundingMode() { return fcsr & 3; /* bits 0-1 */ }
+ private final double getDouble(int r) {
+ return Double.longBitsToDouble(((fpregs[r+1]&0xffffffffL) << 32) | (fpregs[r]&0xffffffffL));
+ }
+ private final void setDouble(int r, double d) {
+ long l = Double.doubleToLongBits(d);
+ fpregs[r+1] = (int)(l >>> 32); fpregs[r] = (int)l;
+ }
+ private final float getFloat(int r) { return Float.intBitsToFloat(fpregs[r]); }
+ private final void setFloat(int r, float f) { fpregs[r] = Float.floatToRawIntBits(f); }
+
+ // Main run loop
+ public void execute() throws EmulationException {
+ int[] r = registers;
+ if(state == PAUSED) state = RUNNING;
+ if(state != RUNNING) throw new IllegalStateException("execute() called in inappropriate state");
+ runSome(nextPC);
+ }
+
+ // Main interpretor (also used for compilation)
+ // the return value is meaningless, its just to catch people typing "return" by accident
+ private int runSome(int pc) throws FaultException,EmulationException {
+ int[] r = registers;
+ int[] f = fpregs;
+ int nextPC = pc + 4;
+ try {
+ OUTER: for(;;) {
+ int insn;
+ try {
+ insn = readPages[pc>>>PAGE_SHIFT][(pc>>>2)&PAGE_WORDS-1];
+ } catch (RuntimeException e) {
+ insn = memRead(pc);
+ }
+
+ int op = (insn >>> 26) & 0xff; // bits 26-31
+ int rs = (insn >>> 21) & 0x1f; // bits 21-25
+ int rt = (insn >>> 16) & 0x1f; // bits 16-20
+ int ft = (insn >>> 16) & 0x1f;
+ int rd = (insn >>> 11) & 0x1f; // bits 11-15
+ int fs = (insn >>> 11) & 0x1f;
+ int shamt = (insn >>> 6) & 0x1f; // bits 6-10
+ int fd = (insn >>> 6) & 0x1f;
+ int subcode = insn & 0x3f; // bits 0-5
+
+ int jumpTarget = (insn & 0x03ffffff); // bits 0-25
+ int unsignedImmediate = insn & 0xffff;
+ int signedImmediate = (insn << 16) >> 16;
+ int branchTarget = signedImmediate;
+
+ int tmp, addr; // temporaries
+
+ r[ZERO] = 0;
+
+ switch(op) {
+ case 0: {
+ switch(subcode) {
+ case 0: // SLL
+ if(insn == 0) break;
+ r[rd] = r[rt] << shamt;
+ break;
+ case 2: // SRL
+ r[rd] = r[rt] >>> shamt;
+ break;
+ case 3: // SRA
+ r[rd] = r[rt] >> shamt;
+ break;
+ // FIXME: Do we need % 32 on the r[rs] ?
+ case 4: // SLLV
+ r[rd] = r[rt] << r[rs];
+ break;
+ case 6: // SRLV
+ r[rd] = r[rt] >>> r[rs];
+ break;
+ case 7: // SRAV
+ r[rd] = r[rt] >> r[rs];
+ break;
+ case 8: // JR
+ tmp = r[rs]; pc += 4; nextPC = tmp;
+ continue OUTER;
+ case 9: // JALR
+ tmp = r[rs]; pc += 4; r[rd] = pc+4; nextPC = tmp;
+ continue OUTER;
+ case 12: // SYSCALL
+ r[V0] = syscall(r[V0],r[A0],r[A1],r[A2],r[A3]);
+ if(state != RUNNING) {
+ this.nextPC = nextPC;
+ break OUTER;
+ }
+ break;
+ case 13: // BREAK
+ throw new EmulationException("Break");
+ case 16: // MFHI
+ r[rd] = hi;
+ break;
+ case 17: // MTHI
+ hi = r[rs];
+ break;
+ case 18: // MFLO
+ r[rd] = lo;
+ break;
+ case 19: // MTLO
+ lo = r[rs];
+ break;
+ case 24: { // MULT
+ long hilo = (long)(r[rs]) * ((long)r[rt]);
+ hi = (int) (hilo >>> 32);
+ lo = (int) hilo;
+ break;
+ }
+ case 25: { // MULTU
+ long hilo = (r[rs] & 0xffffffffL) * (r[rt] & 0xffffffffL);
+ hi = (int) (hilo >>> 32);
+ lo = (int) hilo;
+ break;
+ }
+ case 26: // DIV
+ hi = r[rs]%r[rt];
+ lo = r[rs]/r[rt];
+ break;
+ case 27: // DIVU
+ hi = (int)((r[rs] & 0xffffffffL) % (r[rt] & 0xffffffffL));
+ lo = (int)((r[rs] & 0xffffffffL) / (r[rt] & 0xffffffffL));
+ break;
+ case 32: // ADD
+ r[rd] = r[rs] + r[rt]; // FIXME: Trap on overflow
+ break;
+ case 33: // ADDU
+ r[rd] = r[rs] + r[rt];
+ break;
+ case 34: // SUB
+ r[rd] = r[rs] - r[rt]; // FIXME: Trap on overflow
+ break;
+ case 35: // SUBU
+ r[rd] = r[rs] - r[rt];
+ break;
+ case 36: // AND
+ r[rd] = r[rs] & r[rt];
+ break;
+ case 37: // OR
+ r[rd] = r[rs] | r[rt];
+ break;
+ case 38: // XOR
+ r[rd] = r[rs] ^ r[rt];
+ break;
+ case 39: // NOR
+ r[rd] = ~(r[rs] | r[rt]);
+ break;
+ case 42: // SLT
+ r[rd] = r[rs] < r[rt] ? 1 : 0;
+ break;
+ case 43: // SLTU
+ r[rd] = ((r[rs] & 0xffffffffL) < (r[rt] & 0xffffffffL)) ? 1 : 0;
+ break;
+ default:
+ throw new EmulationException("Illegal instruction 0/" + subcode);
+ }
+ break;
+ }
+ case 1: {
+ switch(rt) {
+ case 0: // BLTZ
+ if(r[rs] < 0) {
+ pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
+ continue OUTER;
+ }
+ break;
+ case 1: // BGEZ
+ if(r[rs] >= 0) {
+ pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
+ continue OUTER;
+ }
+ break;
+ case 16: // BLTZAL
+ if(r[rs] < 0) {
+ pc += 4; r[RA] = pc+4; tmp = pc + branchTarget*4; nextPC = tmp;
+ continue OUTER;
+ }
+ break;
+ case 17: // BGEZAL
+ if(r[rs] >= 0) {
+ pc += 4; r[RA] = pc+4; tmp = pc + branchTarget*4; nextPC = tmp;
+ continue OUTER;
+ }
+ break;
+ default:
+ throw new EmulationException("Illegal Instruction");
+ }
+ break;
+ }
+ case 2: { // J
+ tmp = (pc&0xf0000000) | (jumpTarget << 2);
+ pc+=4; nextPC = tmp;
+ continue OUTER;
+ }
+ case 3: { // JAL
+ tmp = (pc&0xf0000000) | (jumpTarget << 2);
+ pc+=4; r[RA] = pc+4; nextPC = tmp;
+ continue OUTER;
+ }
+ case 4: // BEQ
+ if(r[rs] == r[rt]) {
+ pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
+ continue OUTER;
+ }
+ break;
+ case 5: // BNE
+ if(r[rs] != r[rt]) {
+ pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
+ continue OUTER;
+ }
+ break;
+ case 6: //BLEZ
+ if(r[rs] <= 0) {
+ pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
+ continue OUTER;
+ }
+ break;
+ case 7: //BGTZ
+ if(r[rs] > 0) {
+ pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
+ continue OUTER;
+ }
+ break;
+ case 8: // ADDI
+ r[rt] = r[rs] + signedImmediate;
+ break;
+ case 9: // ADDIU
+ r[rt] = r[rs] + signedImmediate;
+ break;
+ case 10: // SLTI
+ r[rt] = r[rs] < signedImmediate ? 1 : 0;
+ break;
+ case 11: // SLTIU
+ r[rt] = (r[rs]&0xffffffffL) < (unsignedImmediate&0xffffffffL) ? 1 : 0;
+ break;
+ case 12: // ANDI
+ r[rt] = r[rs] & unsignedImmediate;
+ break;
+ case 13: // ORI
+ r[rt] = r[rs] | unsignedImmediate;
+ break;
+ case 14: // XORI
+ r[rt] = r[rs] ^ unsignedImmediate;
+ break;
+ case 15: // LUI
+ r[rt] = unsignedImmediate << 16;
+ break;
+ case 16:
+ throw new EmulationException("TLB/Exception support not implemented");
+ case 17: { // FPU
+ boolean debug = false;
+ String line = debug ? sourceLine(pc) : "";
+ boolean debugon = debug && (line.indexOf("dtoa.c:51") >= 0 || line.indexOf("dtoa.c:52") >= 0 || line.indexOf("test.c") >= 0);
+ if(rs > 8 && debugon)
+ System.out.println(" FP Op: " + op + "/" + rs + "/" + subcode + " " + line);
+ // FIXME: This could probably be removed. I don't think gcc will ever generate code that does this
+ if(roundingMode() != 0 && rs != 6 /*CTC.1*/ && !((rs==16 || rs==17) && subcode == 36 /* CVT.W.Z */))
+ throw new EmulationException("Non-cvt.w.z operation attempted with roundingMode != round to nearest");
+ switch(rs) {
+ case 0: // MFC.1
+ r[rt] = f[rd];
+ break;
+ case 2: // CFC.1
+ if(fs != 31) throw new EmulationException("FCR " + fs + " unavailable");
+ r[rt] = fcsr;
+ break;
+ case 4: // MTC.1
+ f[rd] = r[rt];
+ break;
+ case 6: // CTC.1
+ if(fs != 31) throw new EmulationException("FCR " + fs + " unavailable");
+ fcsr = r[rt];
+ break;
+ case 8: // BC1F, BC1T
+ if(((fcsr&0x800000)!=0) == (((insn>>>16)&1)!=0)) {
+ pc += 4; tmp = pc + branchTarget*4; nextPC = tmp;
+ continue OUTER;
+ }
+ break;
+ case 16: { // Single
+ switch(subcode) {
+ case 0: // ADD.S
+ setFloat(fd,getFloat(fs)+getFloat(ft));
+ break;
+ case 1: // SUB.S
+ setFloat(fd,getFloat(fs)-getFloat(ft));
+ break;
+ case 2: // MUL.S
+ setFloat(fd,getFloat(fs)*getFloat(ft));
+ break;
+ case 3: // DIV.S
+ setFloat(fd,getFloat(fs)/getFloat(ft));
+ break;
+ case 5: // ABS.S
+ setFloat(fd,Math.abs(getFloat(fs)));
+ break;
+ case 6: // MOV.S
+ f[fd] = f[fs];
+ break;
+ case 7: // NEG.S
+ setFloat(fd,-getFloat(fs)); // FIXME: just flip the sign bit
+ break;
+ case 33: // CVT.D.S
+ setDouble(fd,getFloat(fs));
+ break;
+ case 36: // CVT.W.S
+ switch(roundingMode()) {
+ case 0: f[fd] = (int)Math.floor(getFloat(fs)+0.5f); break; // Round to nearest
+ case 1: f[fd] = (int)getFloat(fs); break; // Round towards zero
+ case 2: f[fd] = (int)Math.ceil(getFloat(fs)); break; // Round towards plus infinity
+ case 3: f[fd] = (int)Math.floor(getFloat(fs)); break; // Round towards minus infinity
+ }
+ break;
+ case -50: // C.EQ.S
+ setFC(getFloat(fs) == getFloat(ft)); // FIXME: just compare the ints, be sure things are normalized
+ break;
+ case 60: // C.LT.S
+ setFC(getFloat(fs) < getFloat(ft));
+ break;
+ default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+ }
+ break;
+ }
+ case 17: { // Double
+ switch(subcode) {
+ case 0: // ADD.D
+ setDouble(fd,getDouble(fs)+getDouble(ft));
+ break;
+ case 1: // SUB.D
+ if(debugon) System.out.println("f" + fd + " = f" + fs + " (" + getDouble(fs) + ") - f" + ft + " (" + getDouble(ft) + ")");
+ setDouble(fd,getDouble(fs)-getDouble(ft));
+ break;
+ case 2: // MUL.D
+ if(debugon) System.out.println("f" + fd + " = f" + fs + " (" + getDouble(fs) + ") * f" + ft + " (" + getDouble(ft) + ")");
+ setDouble(fd,getDouble(fs)*getDouble(ft));
+ if(debugon) System.out.println("f" + fd + " = " + getDouble(fd));
+ break;
+ case 3: // DIV.D
+ setDouble(fd,getDouble(fs)/getDouble(ft));
+ break;
+ case 5: // ABS.D
+ setDouble(fd,Math.abs(getDouble(fs)));
+ break;
+ case 6: // MOV.D
+ f[fd] = f[fs];
+ f[fd+1] = f[fs+1];
+ break;
+ case 7: // NEG.D
+ setDouble(fd,-getDouble(fs)); // FIXME: just flip the sign bit
+ break;
+ case 32: // CVT.S.D
+ setFloat(fd,(float)getDouble(fs));
+ break;
+ case 36: // CVT.W.D
+ if(debugon) System.out.println("CVT.W.D rm: " + roundingMode() + " f" + fs + ":" + getDouble(fs));
+ switch(roundingMode()) {
+ case 0: f[fd] = (int)Math.floor(getDouble(fs)+0.5); break; // Round to nearest
+ case 1: f[fd] = (int)getDouble(fs); break; // Round towards zero
+ case 2: f[fd] = (int)Math.ceil(getDouble(fs)); break; // Round towards plus infinity
+ case 3: f[fd] = (int)Math.floor(getDouble(fs)); break; // Round towards minus infinity
+ }
+ if(debugon) System.out.println("CVT.W.D: f" + fd + ":" + f[fd]);
+ break;
+ case 50: // C.EQ.D
+ setFC(getDouble(fs) == getDouble(ft)); // FIXME: just compare the ints, be sure things are normalized
+ break;
+ case 60: // C.LT.D
+ setFC(getDouble(fs) < getDouble(ft));
+ break;
+ case 62: // C.LE.D
+ setFC(getDouble(fs) <= getDouble(ft));
+ break;
+ default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+ }
+ break;
+ }
+ case 20: { // Integer
+ switch(subcode) {
+ case 33: // CVT.D.W
+ setDouble(fd,(double)f[fs]);
+ break;
+ default: throw new EmulationException("Invalid Instruction 17/" + rs + "/" + subcode + " at " + sourceLine(pc));
+ }
+ break;
+ }
+ default:
+ throw new EmulationException("Invalid Instruction 17/" + rs);
+ }
+ break;
+ }
+ case 18: case 19:
+ throw new EmulationException("No coprocessor installed");
+ case 32: { // LB
+ addr = r[rs] + signedImmediate;
+ try {
+ tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+ } catch(RuntimeException e) {
+ tmp = memRead(addr&~3);
+ }
+ switch(addr&3) {
+ case 0: tmp = (tmp>>>24)&0xff; break;
+ case 1: tmp = (tmp>>>16)&0xff; break;
+ case 2: tmp = (tmp>>> 8)&0xff; break;
+ case 3: tmp = (tmp>>> 0)&0xff; break;
+ }
+ if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend
+ r[rt] = tmp;
+ break;
+ }
+ case 33: { // LH
+ addr = r[rs] + signedImmediate;
+ try {
+ tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+ } catch(RuntimeException e) {
+ tmp = memRead(addr&~3);
+ }
+ switch(addr&2) {
+ case 0: tmp = (tmp>>>16)&0xffff; break;
+ case 2: tmp = (tmp>>> 0)&0xffff; break;
+ }
+ if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend
+ r[rt] = tmp;
+ break;
+ }
+ case 34: { // LWL;
+ addr = r[rs] + signedImmediate;
+ try {
+ tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+ } catch(RuntimeException e) {
+ tmp = memRead(addr&~3);
+ }
+ switch(addr&3) {
+ case 0: r[rt] = (r[rt]&0x00000000)|(tmp<< 0); break;
+ case 1: r[rt] = (r[rt]&0x000000ff)|(tmp<< 8); break;
+ case 2: r[rt] = (r[rt]&0x0000ffff)|(tmp<<16); break;
+ case 3: r[rt] = (r[rt]&0x00ffffff)|(tmp<<24); break;
+ }
+ break;
+ }
+ case 35: // LW
+ addr = r[rs] + signedImmediate;
+ try {
+ r[rt] = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+ } catch(RuntimeException e) {
+ r[rt] = memRead(addr);
+ }
+ break;
+ case 36: { // LBU
+ addr = r[rs] + signedImmediate;
+ try {
+ tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+ } catch(RuntimeException e) {
+ tmp = memRead(addr);
+ }
+ switch(addr&3) {
+ case 0: r[rt] = (tmp>>>24)&0xff; break;
+ case 1: r[rt] = (tmp>>>16)&0xff; break;
+ case 2: r[rt] = (tmp>>> 8)&0xff; break;
+ case 3: r[rt] = (tmp>>> 0)&0xff; break;
+ }
+ break;
+ }
+ case 37: { // LHU
+ addr = r[rs] + signedImmediate;
+ try {
+ tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+ } catch(RuntimeException e) {
+ tmp = memRead(addr&~3);
+ }
+ switch(addr&2) {
+ case 0: r[rt] = (tmp>>>16)&0xffff; break;
+ case 2: r[rt] = (tmp>>> 0)&0xffff; break;
+ }
+ break;
+ }
+ case 38: { // LWR
+ addr = r[rs] + signedImmediate;
+ try {
+ tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+ } catch(RuntimeException e) {
+ tmp = memRead(addr&~3);
+ }
+ switch(addr&3) {
+ case 0: r[rt] = (r[rt]&0xffffff00)|(tmp>>>24); break;
+ case 1: r[rt] = (r[rt]&0xffff0000)|(tmp>>>16); break;
+ case 2: r[rt] = (r[rt]&0xff000000)|(tmp>>> 8); break;
+ case 3: r[rt] = (r[rt]&0x00000000)|(tmp>>> 0); break;
+ }
+ break;
+ }
+ case 40: { // SB
+ addr = r[rs] + signedImmediate;
+ try {
+ tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+ } catch(RuntimeException e) {
+ tmp = memRead(addr&~3);
+ }
+ switch(addr&3) {
+ case 0: tmp = (tmp&0x00ffffff) | ((r[rt]&0xff)<<24); break;
+ case 1: tmp = (tmp&0xff00ffff) | ((r[rt]&0xff)<<16); break;
+ case 2: tmp = (tmp&0xffff00ff) | ((r[rt]&0xff)<< 8); break;
+ case 3: tmp = (tmp&0xffffff00) | ((r[rt]&0xff)<< 0); break;
+ }
+ try {
+ writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
+ } catch(RuntimeException e) {
+ memWrite(addr&~3,tmp);
+ }
+ break;
+ }
+ case 41: { // SH
+ addr = r[rs] + signedImmediate;
+ try {
+ tmp = readPages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1];
+ } catch(RuntimeException e) {
+ tmp = memRead(addr&~3);
+ }
+ switch(addr&2) {
+ case 0: tmp = (tmp&0x0000ffff) | ((r[rt]&0xffff)<<16); break;
+ case 2: tmp = (tmp&0xffff0000) | ((r[rt]&0xffff)<< 0); break;
+ }
+ try {
+ writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
+ } catch(RuntimeException e) {
+ memWrite(addr&~3,tmp);
+ }
+ break;
+ }
+ case 42: { // SWL
+ addr = r[rs] + signedImmediate;
+ tmp = memRead(addr&~3);
+ switch(addr&3) {
+ case 0: tmp=(tmp&0x00000000)|(r[rt]>>> 0); break;
+ case 1: tmp=(tmp&0xff000000)|(r[rt]>>> 8); break;
+ case 2: tmp=(tmp&0xffff0000)|(r[rt]>>>16); break;
+ case 3: tmp=(tmp&0xffffff00)|(r[rt]>>>24); break;
+ }
+ try {
+ writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = tmp;
+ } catch(RuntimeException e) {
+ memWrite(addr&~3,tmp);
+ }
+ break;
+ }
+ case 43: // SW
+ addr = r[rs] + signedImmediate;
+ try {
+ writePages[addr>>>PAGE_SHIFT][(addr>>>2)&PAGE_WORDS-1] = r[rt];
+ } catch(RuntimeException e) {
+ memWrite(addr&~3,r[rt]);
+ }
+ break;
+ case 46: { // SWR
+ addr = r[rs] + signedImmediate;
+ tmp = memRead(addr&~3);
+ switch(addr&3) {
+ case 0: tmp=(tmp&0x00ffffff)|(r[rt]<<24); break;
+ case 1: tmp=(tmp&0x0000ffff)|(r[rt]<<16); break;
+ case 2: tmp=(tmp&0x000000ff)|(r[rt]<< 8); break;
+ case 3: tmp=(tmp&0x00000000)|(r[rt]<< 0); break;
+ }
+ memWrite(addr&~3,tmp);
+ break;
+ }
+ case 49: // LWC1
+ f[rt] = memRead(r[rs] + signedImmediate);
+ break;
+ case 57: // SWC1
+ memWrite(r[rs] + signedImmediate,f[rt]);
+ break;
+ default:
+ throw new EmulationException("Invalid Instruction: " + op);
+ }
+ pc = nextPC;
+ nextPC = pc + 4;
+ } // for(;;)
+ } catch(EmulationException e) {
+ this.nextPC = pc;
+ throw e;
+ }
+ return 0;
+ }
+
+ // Image loading function
+ void loadImage(String file) throws IOException {
+ ELF elf = new ELF(file);
+ if(elf.header.type != ELF.ELFHeader.ET_EXEC)
+ throw new IOException("Binary is not an executable");
+ if(elf.header.machine != ELF.ELFHeader.EM_MIPS)
+ throw new IOException("Binary is not for the MIPS I Architecture");
+ entryPoint = elf.header.entry;
+ ELF.PHeader[] pheaders = elf.pheaders;
+ brk = 0;
+ for(int i=0;i<pheaders.length;i++) {
+ ELF.PHeader ph = pheaders[i];
+ if(ph.type != ELF.PHeader.PT_LOAD) continue;
+ int memsize = ph.memsz;
+ int filesize = ph.filesz;
+ if(memsize == 0) continue;
+ if(memsize < 0) throw new IOException("pheader size too large");
+ int addr = ph.vaddr;
+ if(addr == 0x0) throw new IOException("pheader vaddr == 0x0");
+ if(addr+memsize >= (brk<<PAGE_SHIFT)) brk = (addr+memsize+PAGE_SIZE-1) >> PAGE_SHIFT;
+
+ for(int j=0;j<memsize+PAGE_SIZE-1;j+=PAGE_SIZE) {
+ int page = (j+addr) >>> PAGE_SHIFT;
+ if(readPages[page] == null)
+ readPages[page] = new int[PAGE_WORDS];
+ if(ph.writable()) writePages[page] = readPages[page];
+ }
+ if(filesize != 0) {
+ filesize = filesize & ~3;
+ DataInputStream dis = new DataInputStream(ph.getInputStream());
+ do {
+ readPages[addr >>> PAGE_SHIFT][(addr >>> 2)&(PAGE_WORDS-1)] = dis.readInt();
+ addr+=4;
+ filesize-=4;
+ } while(filesize > 0);
+ dis.close();
+ }
+ }
+ image = file;
+ state = INITIALIZED;
+ }
+
+ protected void _start(int pc) {
+ registers[SP] = INITIAL_SP;
+ registers[RA] = 0xdeadbeef;
+ nextPC = pc;
+ }
+ public MIPSInterpreter() { }
+ public MIPSInterpreter(String image) throws IOException { loadImage(image); }
+
+ // Debug functions
+ // NOTE: This probably requires a jdk > 1.1, however, it is only used for debugging
+ public String sourceLine(int pc) {
+ final String addr2line = "mips-unknown-elf-addr2line";
+ String line;
+ if(image==null) return null;
+ try {
+ Process p = Runtime.getRuntime().exec(new String[]{addr2line,"-e",image,toHex(pc)});
+ line = new BufferedReader(new InputStreamReader(p.getInputStream())).readLine();
+ if(line == null) return null;
+ while(line.startsWith("../")) line = line.substring(3);
+ return line;
+ } catch(IOException e) {
+ return null;
+ }
+ }
+
+ public class DebugShutdownHook implements Runnable {
+ public void run() {
+ int pc = nextPC;
+ if(getState() == RUNNING)
+ System.err.print("\nCPU Executing " + toHex(pc) + ": " + sourceLine(pc) + "\n");
+ }
+ }
+
+ public static void main(String[] argv) throws Exception {
+ String image = argv[0];
+ MIPSInterpreter emu = new MIPSInterpreter();
+ emu.loadImage(image);
+ Runtime.getRuntime().addShutdownHook(new Thread(emu.new DebugShutdownHook()));
+ // User data
+ int addr = emu.sbrk(PAGE_SIZE);
+ for(int i=0;i<10;i++) {
+ String s = "User Info item: " + (i+1) + "\0";
+ byte[] b = s.getBytes("US-ASCII");
+ emu.copyout(b,addr,b.length);
+ emu.setUserInfo(i,addr);
+ addr += b.length;
+ }
+ // End user data
+ int status = emu.run(argv);
+ System.err.println("Exit status: " + status);
+ System.exit(status);
+ }
+}
+
+abstract class MIPSEmu implements Syscalls, Errno {
+ // Register Names
+ protected final static int ZERO = 0; // Immutable, hardwired to 0
+ protected final static int AT = 1; // Reserved for assembler
+ protected final static int K0 = 26; // Reserved for kernel
+ protected final static int K1 = 27; // Reserved for kernel
+ protected final static int GP = 28; // Global pointer (the middle of .sdata/.sbss)
+ protected final static int SP = 29; // Stack pointer
+ protected final static int FP = 30; // Frame Pointer
+ protected final static int RA = 31; // Return Address
+
+ // Return values (caller saved)
+ protected final static int V0 = 2;
+ protected final static int V1 = 3;
+ // Argument Registers (caller saved)
+ protected final static int A0 = 4;
+ protected final static int A1 = 5;
+ protected final static int A2 = 6;
+ protected final static int A3 = 7;
+ // Temporaries (caller saved)
+ protected final static int T0 = 8;
+ protected final static int T1 = 9;
+ protected final static int T2 = 10;
+ protected final static int T3 = 11;
+ protected final static int T4 = 12;
+ protected final static int T5 = 13;
+ protected final static int T6 = 14;
+ protected final static int T7 = 15;
+ protected final static int T8 = 24;
+ protected final static int T9 = 25;
+ // Saved (callee saved)
+ protected final static int S0 = 16;
+ protected final static int S1 = 17;
+ protected final static int S2 = 18;
+ protected final static int S3 = 19;
+ protected final static int S4 = 20;
+ protected final static int S5 = 21;
+ protected final static int S6 = 22;
+ protected final static int S7 = 23;
+
+ // Page Constants
+ // Page Size: 4k
+ // Total Pages: 64k
+ // Maxiumum Addressable memory 256mb
+ // 1mb of stack space
+ protected final static int PAGE_SIZE = 4096;
+ protected final static int PAGE_WORDS = (int)(PAGE_SIZE >>> 2);
+ protected final static int PAGE_SHIFT = 12;
+ protected final static int STACK_PAGES = 256;
+ // NOTE: If you change TOTAL_PAGES crt0.c needs to be updated to reflect the
+ // new location of INITIAL_SP
+ protected final static int TOTAL_PAGES = 65536;
+ protected final static int BRK_LIMIT = 32768;
+ // Top page is always empty
+ // next page down contains command line arguments
+ protected final static int ARGS_ADDR = (TOTAL_PAGES-2)*PAGE_SIZE;
+ // next page down contains _user_info data
+ protected final static int USER_INFO_ADDR = (TOTAL_PAGES-3)*PAGE_SIZE;
+ // next page down is the start of the stack
+ protected final static int INITIAL_SP = (TOTAL_PAGES-3)*PAGE_SIZE;
+ // magic page that signified an allocated but empty (untouched) page
+ private final static int[] emptyPage = new int[0];
+
+ // Main memory
+ protected final int[][] readPages;
+ protected final int[][] writePages;
+
+ // Brk
+ protected int brk; // PAGE not address
+
+ // Entry point - what start() sets pc to
+ protected int entryPoint;
+
+ // State constants
+ public final static int UNINITIALIZED = 0;
+ public final static int INITIALIZED = 1;
+ public final static int RUNNING = 2;
+ public final static int PAUSED = 3;
+ public final static int DONE = 4;
+
+ // State
+ protected int state = UNINITIALIZED;
+ public final int getState() { return state; }
+ protected int exitStatus;
+
+ // File descriptors
+ private final static int OPEN_MAX = 256;
+ private FileDescriptor[] fds;
+
+ // Temporary buffer for read/write operations
+ private byte[] _byteBuf = null;
+ private final static int MAX_CHUNK = 4*1024*1024-8;
+
+ // Abstract methods
+ // This should start executing at pc
+ public abstract void execute() throws EmulationException;
+ // This should initialize the cpu registers to point to the entry point
+ protected abstract void _start(int pc);
+
+ public static final int PID = 1;
+
+ public MIPSEmu() {
+ readPages = new int[TOTAL_PAGES][];
+ writePages = new int[TOTAL_PAGES][];
+ for(int i=0;i<STACK_PAGES;i++)
+ readPages[TOTAL_PAGES-1-i] = writePages[TOTAL_PAGES-1-i] = emptyPage;
+ }
+
+ public void copyin(int addr, byte[] a, int length) throws ReadFaultException {
+ int n=0;
+ if((addr&3)!=0) {
+ int word = memRead(addr&~3);
+ switch(addr&3) {
+ case 1: a[n++] = (byte)((word>>>16)&0xff); if(length-n==0) break;
+ case 2: a[n++] = (byte)((word>>> 8)&0xff); if(length-n==0) break;
+ case 3: a[n++] = (byte)((word>>> 0)&0xff); if(length-n==0) break;
+ }
+ addr = (addr&~3)+4;
+ }
+ while(length-n > 3) {
+ int start = (addr&(PAGE_SIZE-1))>>2;
+ int end = start + (min(PAGE_SIZE-(addr&(PAGE_SIZE-1)),(length-n)&~3) >> 2);
+ int[] page = readPages[addr >>> PAGE_SHIFT];
+ if(page == null) throw new ReadFaultException(addr);
+ if(page == emptyPage) { addr+=(end-start); n+=(end-start); continue; }
+ for(int i=start;i<end;i++,addr+=4) {
+ int word = page[i];
+ a[n++] = (byte)((word>>>24)&0xff); a[n++] = (byte)((word>>>16)&0xff);
+ a[n++] = (byte)((word>>> 8)&0xff); a[n++] = (byte)((word>>> 0)&0xff);
+ }
+ }
+ if(length-n > 0) {
+ int word = memRead(addr);
+ if(length-n >= 1) a[n] = (byte)((word>>>24)&0xff);
+ if(length-n >= 2) a[n+1] = (byte)((word>>>16)&0xff);
+ if(length-n >= 3) a[n+2] = (byte)((word>>> 8)&0xff);
+ }
+ }
+
+ public void copyout(byte[] a, int addr, int length) throws FaultException {
+ int n=0;
+ if((addr&3)!=0) {
+ int word = memRead(addr&~3);
+ switch(addr&3) {
+ case 1: word = (word&0xff00ffff)|((a[n]&0xff)<<16); n++; if(length-n==0) break;
+ case 2: word = (word&0xffff00ff)|((a[n]&0xff)<< 8); n++; if(length-n==0) break;
+ case 3: word = (word&0xffffff00)|((a[n]&0xff)<< 0); n++; if(length-n==0) break;
+ }
+ memWrite(addr&~3,word);
+ addr = (addr&~3)+4;
+ }
+
+ while(length-n > 3) {
+ int start = (addr&(PAGE_SIZE-1))>>2;
+ int end = start + (min(PAGE_SIZE-(addr&(PAGE_SIZE-1)),(length-n)&~3) >> 2);
+ int[] page = writePages[addr >>> PAGE_SHIFT];
+ if(page == null) throw new WriteFaultException(addr);
+ if(page == emptyPage) { memWrite(addr,0); page = writePages[addr >>> PAGE_SHIFT]; }
+ for(int i=start;i<end;i++,addr+=4) {
+ int word = ((a[n+0]&0xff)<<24)|((a[n+1]&0xff)<<16)|((a[n+2]&0xff)<<8)|((a[n+3]&0xff)<<0); n+=4;
+ page[i] = word;
+ }
+ }
+ if(length-n > 0) {
+ int word = memRead(addr);
+ word = (word&0x00ffffff)|((a[n]&0xff)<<24);
+ if(length-n > 1) { word = (word&0xff00ffff)|((a[n+1]&0xff)<<16); }
+ if(length-n > 2) { word = (word&0xffff00ff)|((a[n+2]&0xff)<< 8); }
+ memWrite(addr,word);
+ }
+ }
+
+ protected final int memRead(int addr) throws ReadFaultException {
+ if((addr & 3) != 0) throw new ReadFaultException(addr);
+ int page = addr >>> PAGE_SHIFT;
+ int entry = (addr >>> 2) & (PAGE_WORDS-1);
+ try {
+ return readPages[page][entry];
+ } catch(ArrayIndexOutOfBoundsException e) {
+ if(page < 0) throw e; // should never happen
+ if(page > readPages.length) throw new ReadFaultException(addr);
+ if(readPages[page] != emptyPage) throw e; // should never happen
+ initPage(page);
+ return 0;
+ } catch(NullPointerException e) {
+ throw new ReadFaultException(addr);
+ }
+ }
+
+ protected final void memWrite(int addr, int value) throws WriteFaultException {
+ if((addr & 3) != 0) throw new WriteFaultException(addr);
+ int page = addr >>> PAGE_SHIFT;
+ int entry = (addr>>>2)&(PAGE_WORDS-1);
+ try {
+ writePages[page][entry] = value;
+ } catch(ArrayIndexOutOfBoundsException e) {
+ if(page < 0) throw e;// should never happen
+ if(page > writePages.length) throw new WriteFaultException(addr);
+ if(readPages[page] != emptyPage) throw e; // should never happen
+ initPage(page);
+ writePages[page][entry] = value;
+ } catch(NullPointerException e) {
+ throw new WriteFaultException(addr);
+ }
+ }
+
+ protected void initPage(int page) { writePages[page] = readPages[page] = new int[PAGE_WORDS]; }
+
+ public final int exitStatus() {
+ if(state != DONE) throw new IllegalStateException("exitStatus() called in an inappropriate state");
+ return exitStatus;
+ }
+
+ public final int run(String[] args) throws EmulationException {
+ start(args);
+ for(;;) {
+ execute();
+ if(state != PAUSED) break;
+ System.err.println("WARNING: Pause requested while executing run()");
+ try { Thread.sleep(500); } catch(InterruptedException e) { }
+ }
+ if(state != DONE) throw new IllegalStateException("run() ended up in an inappropriate state");
+ return exitStatus();
+ }
+
+ private void addArgs(String[] args) throws EmulationException {
+ if(state == UNINITIALIZED || state == RUNNING || state == PAUSED) throw new IllegalStateException("addArgs() called in inappropriate state");
+ int count = args.length;
+ byte[] nullTerminator = new byte[1];
+ int total = 4; /* null last table entry */
+ for(int i=0;i<count;i++) total += args[i].length() + 1/*null terminator*/ + 4/*table entry*/;
+ if(total > PAGE_SIZE) throw new EmulationException("Arguments too large");
+ int start = ARGS_ADDR;
+ int addr = start + (count+1)*4;
+ int[] table = new int[count+1];
+ for(int i=0;i<count;i++) {
+ byte[] a;
+ try { a = args[i].getBytes("US-ASCII"); } catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
+ table[i] = addr;
+
+ copyout(a,addr,a.length);
+ addr += a.length;
+ copyout(nullTerminator,addr,1);
+ addr += 1;
+
+ }
+ addr=start;
+ for(int i=0;i<count;i++) {
+ memWrite(addr,table[i]);
+ addr += 4;
+ }
+ }
+
+ public void setUserInfo(int index, int word) throws EmulationException {
+ if(index < 0 || index >= 1024) throw new EmulationException("setUserInfo called with index >= 1024");
+ memWrite(USER_INFO_ADDR+index*4,word);
+ }
+
+ public int getUserInfo(int index) throws EmulationException {
+ if(index < 0 || index >= 1024) throw new EmulationException("getUserInfo called with index >= 1024");
+ return memRead(USER_INFO_ADDR+index*4);
+ }
+
+ public final void start(String[] args) throws EmulationException {
+ if(state == UNINITIALIZED || state == RUNNING || state == PAUSED) throw new IllegalStateException("start() called in inappropriate state");
+ _start(entryPoint);
+ addArgs(args);
+ fds = new FileDescriptor[OPEN_MAX];
+ fds[0] = new InputStreamFD(System.in) { public boolean isatty() { return true; } };
+ fds[1] = new OutputStreamFD(System.out) { public boolean isatty() { return true; } };
+ fds[2] = new OutputStreamFD(System.err) { public boolean isatty() { return true; } };
+ state = PAUSED;
+ }
+
+
+ // Syscalls
+ private int write(int fdn, int addr, int count) {
+ int n = 0;
+ int r;
+ FileDescriptor fd;
+ count = Math.min(count,MAX_CHUNK);
+ try {
+ fd = fds[fdn];
+ if(fd == null || !fd.writable()) return -EBADFD;
+ } catch(ArrayIndexOutOfBoundsException e) {
+ return -EBADFD;
+ }
+ try {
+ byte[] buf = byteBuf(count);
+ copyin(addr,buf,count);
+ return fd.write(buf,0,count);
+ } catch(FaultException e) {
+ System.err.println(e);
+ return -EFAULT;
+ } catch(IOException e) {
+ System.err.println(e);
+ return -EIO;
+ }
+ }
+
+ private int read(int fdn, int addr, int count) {
+ FileDescriptor fd;
+ count = Math.min(count,MAX_CHUNK);
+ try {
+ fd = fds[fdn];
+ if(fd == null || !fd.readable()) return -EBADFD;
+ } catch(ArrayIndexOutOfBoundsException e) {
+ return -EBADFD;
+ }
+ try {
+ byte[] buf = byteBuf(count);
+ int n = fd.read(buf,0,count);
+ copyout(buf,addr,n);
+ return n;
+ } catch(FaultException e) {
+ System.err.println(e);
+ return -EFAULT;
+ } catch(IOException e) {
+ System.err.println(e);
+ return -EIO;
+ }
+ }
+
+ private int close(int fdn) {
+ FileDescriptor fd;
+ try {
+ fd = fds[fdn];
+ if(fd == null) return -EBADFD;
+ } catch(ArrayIndexOutOfBoundsException e) {
+ return -EBADFD;
+ }
+ fds[fdn] = null;
+ fd.close();
+ return 0;
+ }
+
+ private int stat(FileInfo fi, int addr) {
+ int size = fi.size();
+ try {
+ memWrite(addr+0,0); // st_dev (top 16), // st_ino (bottom 16)
+ memWrite(addr+4,(fi.type() & 0xf000)|0644); // st_mode
+ memWrite(addr+8,1); // st_nlink (top 16) // st_uid (bottom 16)
+ memWrite(addr+12,0); // st_gid (top 16) // st_rdev (bottom 16)
+ memWrite(addr+16,size); // st_size
+ memWrite(addr+20,0); // st_atime
+ // memWrite(addr+24,0) // st_spare1
+ memWrite(addr+28,(int)(fi.modTime()/1000)); // st_mtime
+ // memWrite(addr+32,0) // st_spare2
+ memWrite(addr+36,0); // st_atime
+ // memWrite(addr+40,0) // st_spare3
+ memWrite(addr+44,512); // st_bklsize;
+ memWrite(addr+48,(size+511)&(~511)); // st_blocks
+ // memWrite(addr+52,0) // st_spare4[0]
+ // memWrite(addr+56,0) // st_spare4[1]
+ } catch(FaultException e) {
+ System.err.println(e);
+ return -EFAULT;
+ }
+ return 0;
+ }
+
+ private int fstat(int fdn, int addr) {
+ FileDescriptor fd;
+ try {
+ fd = fds[fdn];
+ if(fd == null) return -EBADFD;
+ } catch(ArrayIndexOutOfBoundsException e) {
+ return -EBADFD;
+ }
+ return stat(fd.fileInfo(),addr);
+ }
+
+ public int sbrk(int incr) {
+ if(incr==0) return brk<<PAGE_SHIFT;
+ int oldBrk = brk;
+ int newBrk = oldBrk + ((incr+PAGE_SIZE-1)>>PAGE_SHIFT);
+ if(newBrk >= BRK_LIMIT) {
+ System.err.println("Hit BRK_LIMIT");
+ return -ENOMEM;
+ }
+ for(int i=oldBrk;i<newBrk+256;i++)
+ readPages[i] = writePages[i] = emptyPage;
+ brk = newBrk;
+ return oldBrk<<PAGE_SHIFT;
+ }
+
+ private int open(int addr, int flags, int mode) {
+ final int O_RDONLY = 0;
+ final int O_WRONLY = 1;
+ final int O_RDWR = 2;
+ final int O_APPEND = 0x0008;
+ final int O_CREAT = 0x0200;
+ final int O_NONBLOCK = 0x4000;
+ final int O_EXCL = 0x0800;
+
+ if((flags & O_APPEND) != 0) {
+ System.err.println("WARNING: O_APPEND not supported");
+ return -EOPNOTSUPP;
+ }
+ if((flags & O_NONBLOCK) != 0) {
+ System.err.println("WARNING: O_NONBLOCK not supported");
+ return -EOPNOTSUPP;
+ }
+
+ try {
+ int fdn=-1;
+ File f = new File(cstring(addr));
+ System.err.println("Opening: " + f);
+ if((flags & O_EXCL) != 0 && (flags & O_CREAT) != 0)
+ if(!f.createNewFile()) return -EEXIST;
+ if(f.exists()) {
+ if(f.length() >= Integer.MAX_VALUE) return -EOPNOTSUPP;
+ } else {
+ if((flags & O_CREAT) == 0) return -ENOENT;
+ }
+ for(int i=0;i<OPEN_MAX;i++) if(fds[i] == null) { fdn = i; break; }
+ if(fdn==-1) return -ENFILE;
+ fds[fdn] = new RegularFileDescriptor(f,flags&3);
+ return fdn;
+ } catch(FaultException e) {
+ return -EFAULT;
+ } catch(FileNotFoundException e) {
+ if(e.getMessage().indexOf("Permission denied") >= 0) return -EACCES;
+ return -ENOENT;
+ } catch(IOException e) {
+ return -EIO;
+ }
+ }
+
+ private int seek(int fdn, int offset, int whence) {
+ FileDescriptor fd;
+ try {
+ fd = fds[fdn];
+ if(fd == null || !fd.readable()) return -EBADFD;
+ } catch(ArrayIndexOutOfBoundsException e) {
+ return -EBADFD;
+ }
+ try {
+ return fd.seek(offset,whence);
+ } catch(IOException e) {
+ System.err.println(e);
+ return -EPIPE;
+ }
+ }
+
+ // This will only be called by raise() to invoke the default handler
+ // We don't have to worry about actually delivering the signal
+ private int kill(int pid, int signal) {
+ if(pid != PID) return -ESRCH;
+ if(signal < 0 || signal >= 32) return -EINVAL;
+ switch(signal) {
+ case 0: return 0;
+ case 17: // SIGSTOP
+ case 18: // SIGTSTP
+ case 21: // SIGTTIN
+ case 22: // SIGTTOU
+ state = PAUSED;
+ break;
+ case 19: // SIGCONT
+ case 20: // SIGCHLD
+ case 23: // SIGIO
+ case 28: // SIGWINCH
+ break;
+ default: {
+ String msg = "Terminating on signal: " + signal + "\n";
+ exitStatus = 1;
+ state = DONE;
+ if(fds[2]==null) {
+ System.out.print(msg);
+ } else {
+ byte[] b = msg.getBytes();
+ try {
+ fds[2].write(b,0,b.length);
+ } catch(IOException e) { }
+ }
+ }
+ }
+ return 0;
+ }
+
+ private int getpid() { return PID; }
+
+ protected int syscall(int syscall, int a, int b, int c, int d) {
+ switch(syscall) {
+ case SYS_null: return 0;
+ case SYS_exit: exitStatus = a; state = DONE; return 0;
+ case SYS_pause: state = PAUSED; return 0;
+ case SYS_write: return write(a,b,c);
+ case SYS_fstat: return fstat(a,b);
+ case SYS_sbrk: return sbrk(a);
+ case SYS_open: return open(a,b,c);
+ case SYS_close: return close(a);
+ case SYS_read: return read(a,b,c);
+ case SYS_seek: return seek(a,b,c);
+ case SYS_kill: return kill(a,b);
+ case SYS_getpid: return getpid();
+ default:
+ System.err.println("Attempted to use unknown syscall: " + syscall);
+ return -ENOSYS;
+ }
+ }
+
+ // Helper function to read a cstring from main memory
+ private String cstring(int addr) throws ReadFaultException {
+ StringBuffer sb = new StringBuffer();
+ for(;;) {
+ int word = memRead(addr&~3);
+ switch(addr&3) {
+ case 0: if(((word>>>24)&0xff)==0) return sb.toString(); sb.append((char)((word>>>24)&0xff)); addr++;
+ case 1: if(((word>>>16)&0xff)==0) return sb.toString(); sb.append((char)((word>>>16)&0xff)); addr++;
+ case 2: if(((word>>> 8)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 8)&0xff)); addr++;
+ case 3: if(((word>>> 0)&0xff)==0) return sb.toString(); sb.append((char)((word>>> 0)&0xff)); addr++;
+ }
+ }
+ }
+
+ // Exceptions
+ public static class ReadFaultException extends FaultException {
+ public ReadFaultException(int addr) { super(addr); }
+ }
+ public static class WriteFaultException extends FaultException {
+ public WriteFaultException(int addr) { super(addr); }
+ }
+ public static abstract class FaultException extends EmulationException {
+ private int addr;
+ public FaultException(int addr) { this.addr = addr; }
+ public String getMessage() { return "fault at: " + toHex(addr); }
+ }
+ public static class EmulationException extends Exception {
+ public EmulationException() { }
+ public EmulationException(String s) { super(s); }
+ }
+
+ // FileInfo classes - used by stat(2)
+ static class FileInfo {
+ public static final int S_IFIFO = 0010000;
+ public static final int S_FCHR = 0020000;
+ public static final int S_IFDIR = 0040000;
+ public static final int S_IFREG = 0100000;
+
+ public int size() { return 0; }
+ public int type() { return S_IFIFO; }
+ public long modTime() { return 0; }
+ }
+
+ public static class FileFileInfo extends FileInfo {
+ public File f;
+ public FileFileInfo(File f) { this.f = f; }
+ public int size() { return (int)f.length(); }
+ public int type() { return f.isDirectory() ? S_IFDIR : S_IFREG; }
+ public long modTime() { return f.lastModified(); }
+ }
+
+ // File descriptor classes
+ public static abstract class FileDescriptor {
+ public boolean readable() { return false; }
+ public boolean writable() { return false; }
+
+ private static final FileInfo nullFi = new FileInfo();
+ private FileInfo fi;
+ public FileInfo fileInfo() { return fi; }
+
+ FileDescriptor() { this(null); }
+ FileDescriptor(FileInfo fi) { this.fi = fi==null ? nullFi : fi; }
+
+ public int read(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
+ public int write(byte[] a, int off, int length) throws IOException { throw new IOException("no definition"); }
+
+ public int seek(int n, int whence) throws IOException { return -ESPIPE; }
+ public boolean isatty() { return false; }
+
+ void close() { }
+ }
+
+ public static class RegularFileDescriptor extends FileDescriptor {
+ private int mode;
+ private RandomAccessFile raf;
+ public boolean readable() { return mode != 1; }
+ public boolean writable() { return mode != 0; }
+
+ RegularFileDescriptor(File f,int m) throws IOException {
+ super(new FileFileInfo(f));
+ String mode = m == 0 ? "r" : "rw";
+ this.mode = m;
+ raf = new RandomAccessFile(f,mode);
+ if(raf.length() >= Integer.MAX_VALUE) throw new IOException("File too large");
+ }
+
+ public int seek(int n, int whence) throws IOException {
+ final int SEEK_SET = 0;
+ final int SEEK_CUR = 1;
+ final int SEEK_END = 2;
+
+ switch(whence) {
+ case SEEK_SET: break;
+ case SEEK_CUR: n = (int)(raf.getFilePointer()+n); break;
+ case SEEK_END: n = (int)(raf.length()+n); break;
+ default: return -EINVAL;
+ }
+ raf.seek(n);
+ return n;
+ }
+
+ public int write(byte[] a, int off, int length) throws IOException { raf.write(a,off,length); return length; }
+ public int read(byte[] a, int off, int length) throws IOException { int n = raf.read(a,off,length); return n < 0 ? 0 : n; }
+
+ void close() { try { raf.close(); } catch(Exception e) { } }
+ }
+
+ public class OutputStreamFD extends FileDescriptor {
+ private OutputStream os;
+ public boolean writable() { return true; }
+ public OutputStreamFD(OutputStream os) { this.os = os; }
+ public int write(byte[] a, int off, int length) throws IOException { os.write(a,off,length); return length; }
+ }
+
+ public class InputStreamFD extends FileDescriptor {
+ private InputStream is;
+ public boolean readable() { return true; }
+ public InputStreamFD(InputStream is) { this.is = is; }
+ public int read(byte[] a, int off, int length) throws IOException { int n = is.read(a,off,length); return n < 0 ? 0 : n; }
+ }
+
+ // Utility functions
+ private byte[] byteBuf(int size) {
+ if(_byteBuf==null) _byteBuf = new byte[size];
+ else if(_byteBuf.length < size)
+ _byteBuf = new byte[min(max(_byteBuf.length*2,size),MAX_CHUNK+8)];
+ return _byteBuf;
+ }
+ protected final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
+ protected final static int min(int a, int b) { return a < b ? a : b; }
+ protected final static int max(int a, int b) { return a > b ? a : b; }
+}
+
+class ELF {
+ private MyRandomAccessFile fd;
+
+ public ELFHeader header;
+ public PHeader[] pheaders;
+ public SHeader[] sheaders;
+
+ private boolean sectionReaderActive;
+
+ public class ELFHeader {
+ byte klass;
+ byte data;
+ byte osabi;
+ byte abiversion;
+
+ public static final short ET_EXEC = 2;
+ public short type;
+ public static final short EM_MIPS = 8;
+ public short machine;
+ public int version;
+ public int entry;
+ public int phoff;
+ public int shoff;
+ public int flags;
+ public short ehsize;
+ public short phentsize;
+ public short phnum;
+ public short shentsize;
+ public short shnum;
+ public short shstrndx;
+
+ private static final int ELF_MAGIC = 0x7f454c46; // '\177', 'E', 'L', 'F'
+ ELFHeader() throws IOException {
+ if(fd.readInt() != ELF_MAGIC) throw new ELFException("Bad Magic (is: " );
+ klass = fd.readByte();
+ data = fd.readByte();
+ fd.skipFully(1); // version
+ osabi = fd.readByte();
+ abiversion = fd.readByte();
+ fd.skipFully(7);
+ type = fd.readShort();
+ machine = fd.readShort();
+ version = fd.readInt();
+ entry = fd.readInt();
+ phoff = fd.readInt();
+ shoff = fd.readInt();
+ flags = fd.readInt();
+ ehsize = fd.readShort();
+ phentsize = fd.readShort();
+ phnum = fd.readShort();
+ shentsize = fd.readShort();
+ shnum = fd.readShort();
+ shstrndx = fd.readShort();
+ }
+ }
+
+ public class PHeader {
+ public int type;
+ public int offset;
+ public int vaddr;
+ public int paddr;
+ public int filesz;
+ public int memsz;
+ public int flags;
+ public int align;
+
+ public static final int PF_X = 0x1;
+ public static final int PF_W = 0x2;
+ public static final int PF_R = 0x4;
+
+ public static final int PT_LOAD = 1;
+
+ PHeader() throws IOException {
+ type = fd.readInt();
+ offset = fd.readInt();
+ vaddr = fd.readInt();
+ paddr = fd.readInt();
+ filesz = fd.readInt();
+ memsz = fd.readInt();
+ flags = fd.readInt();
+ align = fd.readInt();
+ if(filesz > memsz) throw new ELFException("ELF inconsistency: filesz > memsz");
+ }
+
+ public boolean writable() { return (flags & PF_W) != 0; }
+
+ public InputStream getInputStream() throws IOException {
+ return new BufferedInputStream(new SectionInputStream(
+ offset,offset+filesz));
+ }
+ }
+
+ public class SHeader {
+ int nameidx;
+ public String name;
+ public int type;
+ public int flags;
+ public int addr;
+ public int offset;
+ public int size;
+ public int link;
+ public int info;
+ public int addralign;
+ public int entsize;
+
+ public static final int T_NOBITS = 8;
+
+ SHeader() throws IOException {
+ nameidx = fd.readInt();
+ type = fd.readInt();
+ flags = fd.readInt();
+ addr = fd.readInt();
+ offset = fd.readInt();
+ size = fd.readInt();
+ link = fd.readInt();
+ info = fd.readInt();
+ addralign = fd.readInt();
+ entsize = fd.readInt();
+ }
+
+ public InputStream getInputStream() throws IOException {
+ return new BufferedInputStream(new SectionInputStream(
+ offset, type == T_NOBITS ? 0 : offset+size));
+ }
+ }
+
+ public ELF(String file) throws IOException, ELFException {
+ fd = new MyRandomAccessFile(file,"r");
+ header = new ELFHeader();
+ pheaders = new PHeader[header.phnum];
+ for(int i=0;i<header.phnum;i++) {
+ fd.seek(header.phoff+i*header.phentsize);
+ pheaders[i] = new PHeader();
+ }
+ sheaders = new SHeader[header.shnum];
+ for(int i=0;i<header.shnum;i++) {
+ fd.seek(header.shoff+i*header.shentsize);
+ sheaders[i] = new SHeader();
+ }
+ if(header.shstrndx < 0 || header.shstrndx >= header.shnum) throw new ELFException("Bad shstrndx");
+ fd.seek(sheaders[header.shstrndx].offset);
+ byte[] a = new byte[sheaders[header.shstrndx].size];
+ fd.readFully(a);
+ for(int i=0;i<header.shnum;i++) {
+ SHeader s = sheaders[i];
+ StringBuffer sb = new StringBuffer();
+ for(int off = s.nameidx;off < a.length && a[off] != 0; off++) sb.append((char)a[off]);
+ s.name = sb.toString();
+ }
+ }
+
+ public SHeader sectionWithName(String name) {
+ for(int i=0;i<sheaders.length;i++)
+ if(sheaders[i].name.equals(name))
+ return sheaders[i];
+ return null;
+ }
+
+ public class ELFException extends IOException { ELFException(String s) { super(s); } }
+
+ private class MyRandomAccessFile extends RandomAccessFile {
+ MyRandomAccessFile(String f,String m) throws IOException { super(f,m); }
+ public void skipFully(int n) throws IOException {
+ while(n>0) n-= skipBytes(n);
+ }
+ }
+
+ private class SectionInputStream extends InputStream {
+ private int pos;
+ private int maxpos;
+ SectionInputStream(int start, int end) throws IOException {
+ if(sectionReaderActive)
+ throw new IOException("Section reader already active");
+ sectionReaderActive = true;
+ pos = start;
+ fd.seek(pos);
+ maxpos = end;
+ }
+
+ private int bytesLeft() { return maxpos - pos; }
+ public int read() throws IOException { if(bytesLeft()==0) return -1; int b = fd.read(); if(b >= 0) pos++; return b; }
+ public int read(byte[] b, int off, int len) throws IOException {
+ int n = fd.read(b,off,Math.min(len,bytesLeft())); if(n > 0) pos += n; return n;
+ }
+ public void close() { sectionReaderActive = false; }
+ }
+
+ private static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
+
+ public static void main(String[] args) throws IOException {
+ ELF elf = new ELF(args[0]);
+ System.out.println("Type: " + toHex(elf.header.type));
+ System.out.println("Machine: " + toHex(elf.header.machine));
+ for(int i=0;i<elf.pheaders.length;i++) {
+ ELF.PHeader ph = elf.pheaders[i];
+ System.out.println("PHeader " + toHex(i));
+ System.out.println("\tOffset: " + ph.offset);
+ System.out.println("\tVaddr: " + toHex(ph.vaddr));
+ System.out.println("\tFile Size: " + ph.filesz);
+ System.out.println("\tMem Size: " + ph.memsz);
+ }
+ for(int i=0;i<elf.sheaders.length;i++) {
+ ELF.SHeader sh = elf.sheaders[i];
+ System.out.println("SHeader " + toHex(i));
+ System.out.println("\tName: " + sh.name);
+ System.out.println("\tOffset: " + sh.offset);
+ System.out.println("\tAddr: " + toHex(sh.addr));
+ System.out.println("\tSize: " + sh.size);
+ System.out.println("\tType: " + toHex(sh.type));
+ }
+ }
+}
+interface Errno {
+ public static final int EPERM = 1;
+ public static final int ENOENT = 2;
+ public static final int ESRCH = 3;
+ public static final int EINTR = 4;
+ public static final int EIO = 5;
+ public static final int ENXIO = 6;
+ public static final int ENOEXEC = 8;
+ public static final int EBADF = 9;
+ public static final int ECHILD = 10;
+ public static final int EAGAIN = 11;
+ public static final int ENOMEM = 12;
+ public static final int EACCES = 13;
+ public static final int EFAULT = 14;
+ public static final int ENOTBLK = 15;
+ public static final int EBUSY = 16;
+ public static final int EEXIST = 17;
+ public static final int EXDEV = 18;
+ public static final int ENODEV = 19;
+ public static final int ENOTDIR = 20;
+ public static final int EISDIR = 21;
+ public static final int EINVAL = 22;
+ public static final int ENFILE = 23;
+ public static final int EMFILE = 24;
+ public static final int ENOTTY = 25;
+ public static final int ETXTBSY = 26;
+ public static final int EFBIG = 27;
+ public static final int ENOSPC = 28;
+ public static final int ESPIPE = 29;
+ public static final int EROFS = 30;
+ public static final int EMLINK = 31;
+ public static final int EPIPE = 32;
+ public static final int EDOM = 33;
+ public static final int ERANGE = 34;
+ public static final int ENOMSG = 35;
+ public static final int EIDRM = 36;
+ public static final int ECHRNG = 37;
+ public static final int ELNRNG = 41;
+ public static final int EUNATCH = 42;
+ public static final int ENOCSI = 43;
+ public static final int EDEADLK = 45;
+ public static final int ENOLCK = 46;
+ public static final int EBADE = 50;
+ public static final int EBADR = 51;
+ public static final int EXFULL = 52;
+ public static final int ENOANO = 53;
+ public static final int EBADRQC = 54;
+ public static final int EBADSLT = 55;
+ public static final int EDEADLOCK = 56;
+ public static final int EBFONT = 57;
+ public static final int ENOSTR = 60;
+ public static final int ENODATA = 61;
+ public static final int ETIME = 62;
+ public static final int ENOSR = 63;
+ public static final int ENONET = 64;
+ public static final int ENOPKG = 65;
+ public static final int EREMOTE = 66;
+ public static final int ENOLINK = 67;
+ public static final int EADV = 68;
+ public static final int ESRMNT = 69;
+ public static final int ECOMM = 70;
+ public static final int EPROTO = 71;
+ public static final int EMULTIHOP = 74;
+ public static final int ELBIN = 75;
+ public static final int EDOTDOT = 76;
+ public static final int EBADMSG = 77;
+ public static final int EFTYPE = 79;
+ public static final int ENOTUNIQ = 80;
+ public static final int EBADFD = 81;
+ public static final int EREMCHG = 82;
+ public static final int ELIBACC = 83;
+ public static final int ELIBBAD = 84;
+ public static final int ELIBSCN = 85;
+ public static final int ELIBMAX = 86;
+ public static final int ELIBEXEC = 87;
+ public static final int ENOSYS = 88;
+ public static final int ENMFILE = 89;
+ public static final int ENOTEMPTY = 90;
+ public static final int ENAMETOOLONG = 91;
+ public static final int ELOOP = 92;
+ public static final int EOPNOTSUPP = 95;
+ public static final int EPFNOSUPPORT = 96;
+ public static final int ECONNRESET = 104;
+ public static final int ENOBUFS = 105;
+ public static final int EAFNOSUPPORT = 106;
+ public static final int EPROTOTYPE = 107;
+ public static final int ENOTSOCK = 108;
+ public static final int ENOPROTOOPT = 109;
+ public static final int ESHUTDOWN = 110;
+ public static final int ECONNREFUSED = 111;
+ public static final int EADDRINUSE = 112;
+ public static final int ECONNABORTED = 113;
+ public static final int ENETUNREACH = 114;
+ public static final int ENETDOWN = 115;
+ public static final int ETIMEDOUT = 116;
+ public static final int EHOSTDOWN = 117;
+ public static final int EHOSTUNREACH = 118;
+ public static final int EINPROGRESS = 119;
+ public static final int EALREADY = 120;
+ public static final int EDESTADDRREQ = 121;
+ public static final int EMSGSIZE = 122;
+ public static final int EPROTONOSUPPORT = 123;
+ public static final int ESOCKTNOSUPPORT = 124;
+ public static final int EADDRNOTAVAIL = 125;
+ public static final int ENETRESET = 126;
+ public static final int EISCONN = 127;
+ public static final int ENOTCONN = 128;
+ public static final int ETOOMANYREFS = 129;
+ public static final int EPROCLIM = 130;
+ public static final int EUSERS = 131;
+ public static final int EDQUOT = 132;
+ public static final int ESTALE = 133;
+ public static final int ENOTSUP = 134;
+ public static final int ENOMEDIUM = 135;
+ public static final int ENOSHARE = 136;
+ public static final int ECASECLASH = 137;
+ public static final int EILSEQ = 138;
+ public static final int EOVERFLOW = 139;
+ public static final int __ELASTERROR = 2000;
+}
+interface Syscalls {
+ public static final int SYS_null = 0;
+ public static final int SYS_exit = 1;
+ public static final int SYS_pause = 2;
+ public static final int SYS_open = 3;
+ public static final int SYS_close = 4;
+ public static final int SYS_read = 5;
+ public static final int SYS_write = 6;
+ public static final int SYS_sbrk = 7;
+ public static final int SYS_fstat = 8;
+ public static final int SYS_isatty = 9;
+ public static final int SYS_seek = 10;
+ public static final int SYS_kill = 11;
+ public static final int SYS_getpid = 12;
+}