From a0aa07add21c04f69d6ea5475f45e3e814eb4b4d Mon Sep 17 00:00:00 2001 From: megacz Date: Fri, 30 Jan 2004 07:35:47 +0000 Subject: [PATCH] 2003/09/18 08:10:36 darcs-hash:20040130073547-2ba56-a462866efec88b0bc9884b1d98b27ecc74643d86.gz --- src/org/xwt/mips/Compiler.java | 626 ++++++++++++++ src/org/xwt/mips/Interpreter.java | 1694 +++++++++++++++++++++++++++++++++++++ src/org/xwt/mips/crt0.c | 17 + src/org/xwt/mips/linker.ld | 32 + src/org/xwt/mips/syscalls.c | 78 ++ 5 files changed, 2447 insertions(+) create mode 100644 src/org/xwt/mips/Compiler.java create mode 100644 src/org/xwt/mips/Interpreter.java create mode 100644 src/org/xwt/mips/crt0.c create mode 100644 src/org/xwt/mips/linker.ld create mode 100644 src/org/xwt/mips/syscalls.c diff --git a/src/org/xwt/mips/Compiler.java b/src/org/xwt/mips/Compiler.java new file mode 100644 index 0000000..bcfc662 --- /dev/null +++ b/src/org/xwt/mips/Compiler.java @@ -0,0 +1,626 @@ +// 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() + " "); + 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 0) seek -= dis.skip(seek); + char[] stringTable = new char[p_size[string_table_section_number]]; + for(int i=0; i> 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 numbytes from the stream, emitting case blocks starting at vaddr ofs */ + 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); + } +} + diff --git a/src/org/xwt/mips/Interpreter.java b/src/org/xwt/mips/Interpreter.java new file mode 100644 index 0000000..ddc4d69 --- /dev/null +++ b/src/org/xwt/mips/Interpreter.java @@ -0,0 +1,1694 @@ +// 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= (brk<> PAGE_SHIFT; + + for(int j=0;j>> 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>>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>>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 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 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= 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); + if(newBrk >= BRK_LIMIT) { + System.err.println("Hit BRK_LIMIT"); + return -ENOMEM; + } + for(int i=oldBrk;i= Integer.MAX_VALUE) return -EOPNOTSUPP; + } else { + if((flags & O_CREAT) == 0) return -ENOENT; + } + for(int i=0;i= 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.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;i0) 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 + +extern int _gp[]; +extern int main(int argc, char **argv, char **envp); +extern void exit(int status); + +static char *environ[1] = { NULL }; +static char **argv = (char**)0xfffe000; + +int *_user_info = (int*) 0xfffd000; + +void _start() { + int argc; + __asm__ volatile ("move $28,%0" : : "r"(_gp)); + for(argc=0;argv[argc];argc++); + exit(main(argc,argv,environ)); +} diff --git a/src/org/xwt/mips/linker.ld b/src/org/xwt/mips/linker.ld new file mode 100644 index 0000000..cc6b351 --- /dev/null +++ b/src/org/xwt/mips/linker.ld @@ -0,0 +1,32 @@ +SEARCH_DIR(build) +ENTRY(_start) +STARTUP(org/xwt/imp/crt0.c.o) +INPUT(org/xwt/imp/syscalls.c.o) +GROUP(-lc -lgcc) +__DYNAMIC = 0; + +SECTIONS { + . = 0x10000; + .text : { + *(.init) + *(.text) *(.rodata) *(.rodata.*) *(.eh_frame) + *(.fini) + } + + . = ALIGN(4k); + .data : { + *(.data) + } + . = ALIGN(16); + PROVIDE(_gp = . + 0x8000); + .sdata : { + *(.rosdata) *(.sdata) + } + .sbss : { + *(.sbss) *(.scommon) + } + .bss : { + *(.bss) *(COMMON) + } + _end = .; +} diff --git a/src/org/xwt/mips/syscalls.c b/src/org/xwt/mips/syscalls.c new file mode 100644 index 0000000..ca35ddf --- /dev/null +++ b/src/org/xwt/mips/syscalls.c @@ -0,0 +1,78 @@ +#include +#undef errno + +#include +#include + +// NOTE: This must match up with the Syscalls interface in MIPSInterpreter.java +// (in my tree the Syscalls interface is autogenerated from a syscalls.h. We should +// do the same) +#define SYS_null 0 +#define SYS_exit 1 +#define SYS_pause 2 +#define SYS_open 3 +#define SYS_close 4 +#define SYS_read 5 +#define SYS_write 6 +#define SYS_sbrk 7 +#define SYS_fstat 8 +#define SYS_isatty 9 +#define SYS_seek 10 +#define SYS_kill 11 +#define SYS_getpid 12 + +static inline int syscall4(int n, int a, int b, int c, int d) { + int ret; + __asm__ __volatile__ ( + ".set noreorder\n\t" + "move $2,%1\n\t" + "move $4,%2\n\t" + "move $5,%3\n\t" + "move $6,%4\n\t" + "move $7,%5\n\t" + "syscall\n\t" + "move %0,$2\n\t" + ".set reorder\n\t" + : "=r"(ret) + : "0"(n),"r"(a),"r"(b),"r"(c),"r"(d) + : "$2","$4","$5","$6","$7","memory" + ); + return ret; +} +static inline int syscall0(int n) { return syscall4(n,0,0,0,0); } +static inline int syscall1(int n, int a) { return syscall4(n,a,0,0,0); } +static inline int syscall2(int n, int a, int b) { return syscall4(n,a,b,0,0); } +static inline int syscall3(int n, int a, int b, int c) { return syscall4(n,a,b,c,0); } + +static inline int errnoize(struct _reent *ptr,int n) { + if(n < 0) { + ptr->_errno = -n; + n = -1; + } + return n; +} + +/* These return errno values and must be reentrant */ +caddr_t _sbrk_r(struct _reent *ptr,int incr) { + int n = syscall1(SYS_sbrk,incr); + if(n == -ENOMEM) { ptr->_errno = ENOMEM; return (caddr_t)-1; } + return (caddr_t) n; +} + +int _write_r(struct _reent *ptr,int fd, char *p, int len) { return errnoize(ptr,syscall3(SYS_write,fd,(int)p,len)); } +int _read_r(struct _reent *ptr,int fd, char *p, int len) { return errnoize(ptr,syscall3(SYS_read,fd,(int)p,len)); } +int _close_r(struct _reent *ptr,int fd) { return errnoize(ptr,syscall1(SYS_close,fd)); } +int _fstat_r(struct _reent *ptr,int fd, struct stat *st) { return errnoize(ptr,syscall2(SYS_fstat,fd,(int)st)); } +int _lseek_r(struct _reent *ptr,int fd, int off, int whence) { return errnoize(ptr,syscall3(SYS_seek,fd,off,whence)); } +int _open_r(struct _reent *ptr,char *name, int flags, int mode) { return errnoize(ptr,syscall3(SYS_open,(int)name,flags,mode)); } +int _kill_r(struct _reent *ptr,pid_t pid, int sig) { return errnoize(ptr,syscall2(SYS_kill,(int)pid,sig)); } +int _getpid_r(struct _reent *ptr) { return errnoize(ptr,syscall0(SYS_getpid)); } + +/* No errno values */ +void _exit(int status) { + syscall1(SYS_exit,status); + for(;;); /* shut up gcc */ +} +int isatty(int fd) { return syscall1(SYS_isatty,fd); } + +void emu_pause() { syscall0(SYS_pause); } -- 1.7.10.4