package TAM; /** * TAM Assembler. * * (C) Univerity of Twente, * Department of Computer Science, * Formal Methods & Tools Group, * Enschede, The Netherlands. * * contact: Theo C. Ruys (email: ruys@cs.utwente.nl) * * @author Matthijs Bomhoff (initial design and implementation) * Theo Ruys (minor tweaking) * @version 2003.04.13 */ /* CHANGES * o 2003.04.07 bomhoff Initial version. * o 2003.04.13 ruys - Changed package name from triangle.assembler * to TAM. * - Relative addresses can be negative: * added -? in the front of group 6 (d). * - Added optional line-comments to a label and * assembler line. * - Added toString() to class Instruction for * debugging the Assembler. */ import java.util.*; import java.io.*; import java.util.regex.*; public class Assembler { protected static final Matcher commandMatcher = Pattern.compile( "^\\s*" + //whitespace "(([A-Za-z0-9_]+):)?" + //optional line number or label (group 2) "\\s*" + //whitespace "([A-Z]+)" + //opcode mnemonic (group 3) "\\s*" + //whitespace "(\\(([a-zA-Z0-9]+)\\))?" + //n (group 5) "\\s*" + //whitespace "(-?[a-zA-Z0-9]+)?" + //d (group 6) "\\s*" + //whitespace "(\\[([a-zA-Z0-9]+)\\])?" + //r (group 8) "\\s*" + //whitespace "(;.*)?" + // optional comment (group 10) "\\s*$" //whitespace ).matcher(""); protected static final Matcher labelMatcher = Pattern.compile( "^\\s*" + //whitespace "([A-Za-z0-9_]+)" + //labels (group 1) ":" + //colon "\\s*" + //whitespace "(;.*)?" + // optional comment (group 2) "\\s*$" //whitespace ).matcher(""); protected static final Map labelMap = new HashMap(); public static void main(String[] args) throws Exception { InputStream in = null; OutputStream out = null; if (args.length == 0) { in = System.in; out = System.out; } else if (args.length == 2) { in = new FileInputStream(args[0]); out = new FileOutputStream(args[1]); } else { System.err.println("usage: Assemble [infile outfile]"); System.exit(1); } assemble(in, out); in.close(); out.close(); System.exit(0); } public static void assemble(InputStream in, OutputStream out) throws IOException { final BufferedReader buf = new BufferedReader(new InputStreamReader(in)); final DataOutputStream dataOut = new DataOutputStream(out); final List lines = new ArrayList(); String line = null; //read file into buffer while ( (line = buf.readLine()) != null) { lines.add(line); } final int totalLineCount = lines.size(); int labelLineCount = 0; //first pass, extract labels int codeLineNumber = 0; Iterator iter = lines.iterator(); while ( iter.hasNext() ) { line = (String) iter.next(); labelMatcher.reset(line); commandMatcher.reset(line); if (labelMatcher.matches()) { labelMap.put(labelMatcher.group(1), new Integer(codeLineNumber)); labelLineCount++; iter.remove(); //remove labels after adding them to the label map } else if (commandMatcher.matches()) { if (commandMatcher.group(2) != null) { labelMap.put(commandMatcher.group(2), new Integer(codeLineNumber)); } codeLineNumber++; } else { //neither label, nor code, simply remove the line iter.remove(); } } //only _real_ code lines should be left in lines now final int codeLineCount = lines.size(); //second pass, emit code iter = lines.iterator(); while ( iter.hasNext() ) { line = (String) iter.next(); Instruction instruction = instructionFromString(line); if (instruction != null) { instruction.write(dataOut); } } System.err.println("Assembly results:\n" + "lines in file: " + totalLineCount + "\n" + "lines of code: " + codeLineCount + "\n" + "label lines : " + labelLineCount + "\n" + "lines ignored: " + (totalLineCount - codeLineCount - labelLineCount) + "\n" ); } public static Instruction instructionFromString(String str) { Instruction result = null; commandMatcher.reset(str); //process command if (commandMatcher.matches()) { try { final String opString = commandMatcher.group(3); final String nString = commandMatcher.group(5); final String dString = commandMatcher.group(6); final String rString = commandMatcher.group(8); final int op = getOpcode(opString); int n = 0; int d = 0; int r = 0; switch (op) { case 0: //LOAD n = Integer.parseInt(nString); d = Integer.parseInt(dString); r = getRegister(rString); result = new Instruction(op, r, n, d); break; case 1: //LOADA d = Integer.parseInt(dString); r = getRegister(rString); result = new Instruction(op, r, n, d); break; case 2: //LOADI n = Integer.parseInt(nString); result = new Instruction(op, r, n, d); break; case 3: //LOADL d = Integer.parseInt(dString); result = new Instruction(op, r, n, d); break; case 4: //STORE n = Integer.parseInt(nString); d = Integer.parseInt(dString); r = getRegister(rString); result = new Instruction(op, r, n, d); break; case 5: //STOREI n = Integer.parseInt(nString); result = new Instruction(op, r, n, d); break; case 6: //CALL if (isSubroutine(dString)) { n = getRegister("SB"); d = getSubroutine(dString); r = getRegister("PB"); } else { n = getRegister(nString); final Integer labelLocation = (Integer) labelMap.get(dString); if (labelLocation != null) { d = labelLocation.intValue(); } else { d = Integer.parseInt(dString); } r = getRegister(rString); } result = new Instruction(op, r, n, d); break; case 7: //CALLI result = new Instruction(op, r, n, d); break; case 8: //RETURN n = Integer.parseInt(nString); d = Integer.parseInt(dString); result = new Instruction(op, r, n, d); break; case 10: //PUSH d = Integer.parseInt(dString); result = new Instruction(op, r, n, d); break; case 11: //POP n = Integer.parseInt(nString); d = Integer.parseInt(dString); result = new Instruction(op, r, n, d); break; case 12: //JUMP { final Integer labelLocation = (Integer) labelMap.get(dString); if (labelLocation != null) { d = labelLocation.intValue(); } else { d = Integer.parseInt(dString); } } r = getRegister(rString); result = new Instruction(op, r, n, d); break; case 13: //JUMPI result = new Instruction(op, r, n, d); break; case 14: //JUMPIF n = Integer.parseInt(nString); { final Integer labelLocation = (Integer) labelMap.get(dString); if (labelLocation != null) { d = labelLocation.intValue(); } else { d = Integer.parseInt(dString); } } r = getRegister(rString); result = new Instruction(op, r, n, d); break; case 15: //HALT result = new Instruction(op, r, n, d); break; default: System.err.println("ERROR: opcode number " + op + "(\"" + opString + "\") unknown"); break; } } catch (Exception e) { System.err.println("ERROR: an " + e.getClass().toString().substring(6) + " occured, while parsing the line \"" + str + "\"... skipping..."); } } else { System.err.println("ERROR: line \"" + str + "\" does not match the (dis)assembler format... skipping..."); } return result; } /** * An inner class used to represent a single opcode */ public static final class Instruction { public int op; public int r; public int n; public int d; public Instruction(int op, int r, int n, int d) { this.op = op; this.r = r; this.n = n; this.d = d; } public void write(DataOutputStream out) throws IOException { out.writeInt(op); out.writeInt(r); out.writeInt(n); out.writeInt(d); } public String toString() { return "" + op + "(" + n + ") " + d + "[" + r + "]"; } } /* * Some functions to map strings to numbers */ public static int getOpcode(String mnemonic) throws IllegalArgumentException { Integer integer = (Integer) opcodeMap.get(mnemonic); if (integer == null) throw new IllegalArgumentException("Opcode \"" + mnemonic + "\" unknown"); return integer.intValue(); } public static int getRegister(String name) throws IllegalArgumentException { Integer integer = (Integer) registerMap.get(name); if (integer == null) throw new IllegalArgumentException("Register \"" + name + "\" unknown"); return integer.intValue(); } public static boolean isSubroutine(String identifier) { return subroutineMap.containsKey(identifier); } public static int getSubroutine(String identifier) throws IllegalArgumentException { Integer integer = (Integer) subroutineMap.get(identifier); if (integer == null) throw new IllegalArgumentException("Subroutine \"" + identifier + "\" unknown"); return integer.intValue(); } /* * Here comes the ugly part: */ private static final Map opcodeMap = new LinkedHashMap(); private static final Map registerMap = new LinkedHashMap(); private static final Map subroutineMap = new LinkedHashMap(); static { opcodeMap.put("LOAD", new Integer(0)); opcodeMap.put("LOADA", new Integer(1)); opcodeMap.put("LOADI", new Integer(2)); opcodeMap.put("LOADL", new Integer(3)); opcodeMap.put("STORE", new Integer(4)); opcodeMap.put("STOREI", new Integer(5)); opcodeMap.put("CALL", new Integer(6)); opcodeMap.put("CALLI", new Integer(7)); opcodeMap.put("RETURN", new Integer(8)); opcodeMap.put("PUSH", new Integer(10)); opcodeMap.put("POP", new Integer(11)); opcodeMap.put("JUMP", new Integer(12)); opcodeMap.put("JUMPI", new Integer(13)); opcodeMap.put("JUMPIF", new Integer(14)); opcodeMap.put("HALT", new Integer(15)); registerMap.put("CB", new Integer(0)); registerMap.put("CT", new Integer(1)); registerMap.put("PB", new Integer(2)); registerMap.put("PT", new Integer(3)); registerMap.put("SB", new Integer(4)); registerMap.put("ST", new Integer(5)); registerMap.put("HB", new Integer(6)); registerMap.put("HT", new Integer(7)); registerMap.put("LB", new Integer(8)); registerMap.put("L1", new Integer(8 + 1)); registerMap.put("L2", new Integer(8 + 2)); registerMap.put("L3", new Integer(8 + 3)); registerMap.put("L4", new Integer(8 + 4)); registerMap.put("L5", new Integer(8 + 5)); registerMap.put("L6", new Integer(8 + 6)); registerMap.put("CP", new Integer(15)); subroutineMap.put("id", new Integer(1)); subroutineMap.put("not", new Integer(2)); subroutineMap.put("and", new Integer(3)); subroutineMap.put("or", new Integer(4)); subroutineMap.put("succ", new Integer(5)); subroutineMap.put("pred", new Integer(6)); subroutineMap.put("neg", new Integer(7)); subroutineMap.put("add", new Integer(8)); subroutineMap.put("sub", new Integer(9)); subroutineMap.put("mult", new Integer(10)); subroutineMap.put("div", new Integer(11)); subroutineMap.put("mod", new Integer(12)); subroutineMap.put("lt", new Integer(13)); subroutineMap.put("le", new Integer(14)); subroutineMap.put("ge", new Integer(15)); subroutineMap.put("gt", new Integer(16)); subroutineMap.put("eq", new Integer(17)); subroutineMap.put("ne", new Integer(18)); subroutineMap.put("eol", new Integer(19)); subroutineMap.put("eof", new Integer(20)); subroutineMap.put("get", new Integer(21)); subroutineMap.put("put", new Integer(22)); subroutineMap.put("geteol", new Integer(23)); subroutineMap.put("puteol", new Integer(24)); subroutineMap.put("getint", new Integer(25)); subroutineMap.put("putint", new Integer(26)); subroutineMap.put("new", new Integer(27)); subroutineMap.put("dispose", new Integer(28)); subroutineMap.put("fopen", new Integer(29)); subroutineMap.put("fgetint", new Integer(30)); subroutineMap.put("fputint", new Integer(31)); subroutineMap.put("fget", new Integer(32)); subroutineMap.put("fput", new Integer(33)); subroutineMap.put("fgeteol", new Integer(34)); subroutineMap.put("fputeol", new Integer(35)); subroutineMap.put("feol", new Integer(36)); subroutineMap.put("feof", new Integer(37)); subroutineMap.put("fclose", new Integer(38)); } }