/* JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine Copyright (C) 2012-2013 Ian Preston This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Details (including contact information) can be found at: jpc.sourceforge.net or the developer website sourceforge.net/projects/jpc/ End of licence header */ package tools; import java.io.*; import java.util.*; public class Opcode { public static String HEADER; static { try { String tmp = ""; BufferedReader r = new BufferedReader(new FileReader("HEADER")); String line; while ((line = r.readLine()) != null) tmp += line + "\n"; HEADER = tmp + "\n"; } catch (IOException e) { e.printStackTrace(); } } public static final boolean DEBUG_SIZE = true; final String name; final Operand[] operands; final String snippet; final String ret; final int size; final boolean multiSize; final boolean isMem, isBranch, needsSegment; private Opcode(String mnemonic, String[] args, int size, String snippet, String ret, boolean isMem, boolean needsSegment) { this.needsSegment = needsSegment; boolean msize = false; for (String s: args) if (s.equals("Ev") || s.equals("Gv") || s.equals("Iv") || s.equals("Iz") || s.equals("Ov") && !mnemonic.contains("o16") && !mnemonic.contains("o32")) msize = true; multiSize = msize; operands = new Operand[args.length]; for (int i=0; i < operands.length; i++) operands[i] = Operand.get(args[i], size, isMem); StringBuilder tmp = new StringBuilder(); tmp.append(mnemonic); for (int i=0; i < operands.length; i++) { tmp.append("_"); tmp.append(operands[i]); } if (isMem) tmp.append("_mem"); this.isMem = isMem; name = tmp.toString(); this.snippet = snippet; this.ret = ret; this.size = size; isBranch = !ret.startsWith("Branch.None"); } public String getName() { return name; } public String getSource(String mode) { StringBuilder b = new StringBuilder(); b.append(getPreamble(mode)); if (needsSegment) b.append(" final int segIndex;\n"); for (int i=0; i < operands.length; i++) b.append(operands[i].define(i+1)); if (isBranch) { b.append(" final int blockLength;\n"); b.append(" final int instructionLength;\n"); } if (multiSize) b.append(" final int size;\n"); //b.append(getConstructor()); b.append(getDirectConstructor()); b.append(getExecute()); b.append(getBranch()); b.append(getToString()); b.append("}"); return b.toString(); } private String processSnippet(int size) { String body = snippet; if (operands.length > 0) { if (body.contains("getF") || body.contains("setF")) { body = body.replaceAll("\\$op1.getF", operands[0].getF(1)); body = body.replaceAll("\\$op1.setF", operands[0].setF(1)); if (operands.length >1) { body = body.replaceAll("\\$op2.getF", operands[1].getF(2)); body = body.replaceAll("\\$op2.setF", operands[1].setF(2)); if (operands.length > 2) { body = body.replaceAll("\\$op3.getF", operands[2].getF(3)); body = body.replaceAll("\\$op3.setF", operands[2].setF(3)); } } } if (body.contains("getA") || body.contains("setA")) { body = body.replaceAll("\\$op1.getA", operands[0].getA(1)); body = body.replaceAll("\\$op1.setA", operands[0].setA(1)); if ((operands.length >1) && (body.contains("2.getA") || body.contains("2.setA"))) { body = body.replaceAll("\\$op2.getA", operands[1].getA(2)); body = body.replaceAll("\\$op2.setA", operands[1].setA(2)); if (operands.length > 2) { body = body.replaceAll("\\$op3.getA", operands[2].getA(3)); body = body.replaceAll("\\$op3.setA", operands[2].setA(3)); } } } if (body.contains("op1.get16") || body.contains("op1.set16")) { body = body.replaceAll("\\$op1.get16", operands[0].get16(1)); body = body.replaceAll("\\$op1.set16", operands[0].set16(1)); } if (body.contains("op2.get16") || body.contains("op2.set16")) { if (operands.length >1) { body = body.replaceAll("\\$op2.get16", operands[1].get16(2)); body = body.replaceAll("\\$op2.set16", operands[1].set16(2)); } } if (body.contains("op1.get32") || body.contains("op1.set32")) { body = body.replaceAll("\\$op1.get32", operands[0].get32(1)); body = body.replaceAll("\\$op1.set32", operands[0].set32(1)); } if (body.contains("op2.get32") || body.contains("op2.set32")) { if (operands.length >1) { body = body.replaceAll("\\$op2.get32", operands[1].get32(2)); body = body.replaceAll("\\$op2.set32", operands[1].set32(2)); } } body = body.replaceAll("\\$op1.get", operands[0].get(1)); body = body.replaceAll("\\$op1.set", operands[0].set(1)); if (operands.length >1) { body = body.replaceAll("\\$op2.get", operands[1].get(2)); body = body.replaceAll("\\$op2.set", operands[1].set(2)); if (operands.length > 2) { body = body.replaceAll("\\$op3.get", operands[2].get(3)); body = body.replaceAll("\\$op3.set", operands[2].set(3)); } } } body = body.replaceAll("\\$size", size+""); if ((name.startsWith("mul_") || name.startsWith("div_"))&& (size == 32)) { body = body.replaceAll("\\$mask", "0xFFFFFFFFL & "); body = body.replaceAll("\\$cast", "(int)"); } else { body = body.replaceAll("\\$cast", getCast(size)); if (body.contains("mask2")) body = body.replaceAll("\\$mask2", getMask(operands[1].getSize())); if (body.contains("mask1")) body = body.replaceAll("\\$mask1", getMask(operands[0].getSize())); body = body.replaceAll("\\$mask", getMask(size)); } return body; } private String getCast(int size) { if (size == 8) return "(byte)"; else if (size == 16) return "(short)"; return ""; } private String getMask(int size) { if (size == 8) return "0xFF&"; else if (size == 16) return "0xFFFF&"; return ""; } private String getExecute() { StringBuilder b = new StringBuilder(); b.append(" public Branch execute(Processor cpu)\n {\n"); for (int i=0; i < operands.length; i++) { String load = operands[i].load(i+1); if (load.length() > 0) b.append(load+"\n"); } if (needsSegment) b.append(" Segment seg = cpu.segs[segIndex];\n"); if (multiSize) { b.append(" if (size == 16)\n {\n"); b.append(processSnippet(16)); b.append("\n }\n else if (size == 32)\n {\n"); for (int i=0; i < operands.length; i++) operands[i] = Operand.get(operands[i].toString(), 32, isMem); b.append(processSnippet(32)); b.append("\n }"); if (DEBUG_SIZE) { b.append(" else throw new IllegalStateException(\"Unknown size \"+size);"); } } else b.append(processSnippet(size)); if (ret.trim().length() > 0) b.append("\n return "+ret+";"); b.append("\n }\n\n"); return b.toString(); } private String getConstructor() { StringBuilder b = new StringBuilder(); b.append("\n public "+getName()+"(int blockStart, Instruction parent)\n {\n super(blockStart, parent);\n"); if (needsSegment) { b.append(" segIndex = Processor.getSegmentIndex(parent.getSegment());\n"); } if (multiSize) { /*int vIndex = -1; for (int i=operands.length-1; i >= 0; i--) { String arg = operands[i].toString(); if ((arg.length() > 1) && arg.charAt(1) == 'v') vIndex = i; if ((arg.length() > 1) && arg.equals("Iz")) vIndex = i; if (Operand.reg16.containsKey(arg) && (vIndex == -1)) vIndex = i; } if (vIndex == -1) throw new IllegalStateException("Couldn't find multisize operand "+toString());*/ b.append(" size = parent.opr_mode;\n");//parent.operand["+vIndex+"].size;\n"); } if (isBranch) { b.append(" blockLength = parent.x86Length+(int)parent.eip-blockStart;\n"); b.append(" instructionLength = parent.x86Length;\n"); } for (int i=0; i < operands.length; i++) { String cons = operands[i].construct(i+1); if (cons.length() > 0) b.append(cons + "\n"); } b.append(" }\n\n"); return b.toString(); } private String getDirectConstructor() { StringBuilder b = new StringBuilder(); b.append("\n public "+getName()+"("+DecoderGenerator.argsDef+")\n {\n super(blockStart, eip);\n"); if (needsModrm()) b.append(" int modrm = input.readU8();\n"); if (needsSegment) b.append(" segIndex = Prefices.getSegment(prefices, Processor.DS_INDEX);\n"); if (multiSize) { //b.append(" size = parent.opr_mode;\n");//parent.operand["+vIndex+"].size;\n"); } for (int i=0; i < operands.length; i++) { String cons = operands[i].directConstruct(i+1); if (cons.length() > 0) b.append(cons + "\n"); } if (isBranch) { b.append(" instructionLength = (int)input.getAddress()-eip;\n"); b.append(" blockLength = eip-blockStart+instructionLength;\n"); } b.append(" }\n\n"); return b.toString(); } private boolean needsModrm() { for (Operand operand: operands) { if (operand instanceof Operand.Address) return true; else if (operand instanceof Operand.ControlReg) return true; else if (operand instanceof Operand.DebugReg) return true; else if (operand instanceof Operand.Mem) return operand.needsModrm(); else if (operand instanceof Operand.Reg) return true; else if (operand instanceof Operand.Segment) return true; else if (operand instanceof Operand.STi) return true; else if (operand instanceof Operand.FarMemPointer) return true; } return false; } private String getCopywriteHeader() { return HEADER; } private String getPreamble(String mode) { return getCopywriteHeader() + "package org.jpc.emulator.execution.opcodes."+mode+";\n\nimport org.jpc.emulator.execution.*;\nimport org.jpc.emulator.execution.decoder.*;\nimport org.jpc.emulator.processor.*;\nimport org.jpc.emulator.processor.fpu64.*;\nimport static org.jpc.emulator.processor.Processor.*;\n\npublic class "+getName()+" extends Executable\n{\n"; } private String getBranch() { if (ret.equals("Branch.None")) return " public boolean isBranch()\n {\n return false;\n }\n\n"; else return " public boolean isBranch()\n {\n return true;\n }\n\n"; } private String getToString() { return " public String toString()\n {\n return this.getClass().getName();\n }\n"; } public void writeToFile(String mode) { try { BufferedWriter w = new BufferedWriter(new FileWriter("src/org/jpc/emulator/execution/opcodes/"+mode+"/"+getName()+".java")); w.write(getSource(mode)); w.flush(); w.close(); } catch (IOException e) {e.printStackTrace();} } private static boolean isMem(String[] args) { for (String arg: args) if (arg.equals("Eb") || arg.equals("Ew") || arg.equals("Ed") || arg.equals("Ob") || arg.equals("Ow") || arg.equals("Od") || arg.equals("M") || arg.equals("R")) return true; return false; } private static boolean isMemOnly(String[] args) { for (String arg: args) if (arg.equals("Ep")) return true; if ((args.length == 1) && (args[0].equals("Mw") || args[0].equals("Md") || args[0].equals("Mq") || args[0].equals("Mt"))) return true; return false; } public static List<String> enumerateArg(String arg) { List<String> res = new LinkedList(); if (arg.equals("STi")) { res.add("ST0"); res.add("ST1"); res.add("ST2"); res.add("ST3"); res.add("ST4"); res.add("ST5"); res.add("ST6"); res.add("ST7"); } else res.add(arg); return res; } public static List<String[]> enumerateArgs(String[] in) { List<String[]> res = new LinkedList(); List<String[]> next = new LinkedList(); res.add(in); for (int i=0; i < in.length; i++) { for (String[] args: res) { for (String arg: enumerateArg(args[i])) { String[] tmp = new String[args.length]; System.arraycopy(args, 0, tmp, 0, tmp.length); tmp[i] = arg; next.add(tmp); } } res = next; if (i < in.length-1) next = new LinkedList(); } if (in.length == 0) next.add(new String[0]); return next; } public static List<Opcode> get(String mnemonic, String[] args, int size, String snippet, String ret, boolean segment, boolean singleType, boolean mem) { List<Opcode> ops = new LinkedList(); if (isMemOnly(args)) { ops.add(new Opcode(mnemonic, args, size, snippet, ret, true, segment)); return ops; } for (String[] eachArgs: enumerateArgs(args)) { if (!singleType || (singleType && !mem)) ops.add(new Opcode(mnemonic, eachArgs, size, snippet, ret, false, segment)); if (!singleType || (singleType && mem)) if (isMem(args)) ops.add(new Opcode(mnemonic, eachArgs, size, snippet, ret, true, segment)); } return ops; } }