|
| 1 | +/** |
| 2 | + * generate human readable mJAM instructions |
| 3 | + * @author prins |
| 4 | + * @version COMP 520 V2.2 |
| 5 | + */ |
| 6 | +package mJAM; |
| 7 | + |
| 8 | +import java.io.FileWriter; |
| 9 | +import java.io.IOException; |
| 10 | +import java.util.HashMap; |
| 11 | +import java.util.Map; |
| 12 | +import java.util.SortedSet; |
| 13 | +import java.util.TreeSet; |
| 14 | + |
| 15 | +/** |
| 16 | + * Disassemble the mJAM object code |
| 17 | + * from input file xxx.mJAM |
| 18 | + * into output file xxx.asm |
| 19 | + * |
| 20 | + * @author prins |
| 21 | + * @version COMP 520 v2.2 |
| 22 | + */ |
| 23 | +public class Disassembler { |
| 24 | + |
| 25 | + private String objectFileName; |
| 26 | + private String asmName; |
| 27 | + private FileWriter asmOut; |
| 28 | + private boolean error = false; |
| 29 | + private Map<Integer, String> addrToLabel; |
| 30 | + |
| 31 | + public Disassembler(String objectFileName) { |
| 32 | + this.objectFileName = objectFileName; |
| 33 | + } |
| 34 | + |
| 35 | + /** |
| 36 | + * Writes the r-field of an instruction in the form "l<I>reg</I>r", where |
| 37 | + * l and r are the bracket characters to use. |
| 38 | + * @param leftbracket the character to print before the register. |
| 39 | + * @param r the number of the register. |
| 40 | + * @param rightbracket the character to print after the register. |
| 41 | + */ |
| 42 | + private void writeR(char leftbracket, int r, char rightbracket) { |
| 43 | + asmWrite(Character.toString(leftbracket)); |
| 44 | + asmWrite(Machine.intToReg[r].toString()); |
| 45 | + asmWrite(Character.toString(rightbracket)); |
| 46 | + } |
| 47 | + |
| 48 | + /** |
| 49 | + * Writes a void n-field of an instruction. |
| 50 | + */ |
| 51 | + private void blankN() { |
| 52 | + asmWrite(" "); |
| 53 | + } |
| 54 | + |
| 55 | + // Writes the n-field of an instruction. |
| 56 | + /** |
| 57 | + * Writes the n-field of an instruction in the form "(n)". |
| 58 | + * @param n the integer to write. |
| 59 | + */ |
| 60 | + private void writeN(int n) { |
| 61 | + asmWrite(String.format("%-6s","(" + n + ")")); |
| 62 | + } |
| 63 | + |
| 64 | + /** |
| 65 | + * Writes the d-field of an instruction. |
| 66 | + * @param d the integer to write. |
| 67 | + */ |
| 68 | + private void writeD(int d) { |
| 69 | + asmWrite(Integer.toString(d)); |
| 70 | + } |
| 71 | + |
| 72 | + /** |
| 73 | + * Writes the name of primitive routine with relative address d. |
| 74 | + * @param d the displacment of the primitive routine. |
| 75 | + */ |
| 76 | + private void writePrimitive(int d) { |
| 77 | + Machine.Prim prim = Machine.intToPrim[d]; |
| 78 | + asmWrite(String.format("%-8s",prim.toString())); |
| 79 | + } |
| 80 | + |
| 81 | + /** |
| 82 | + * Writes the given instruction in assembly-code format. |
| 83 | + * @param instr the instruction to display. |
| 84 | + */ |
| 85 | + private void writeInstruction(Instruction instr) { |
| 86 | + |
| 87 | + String targetLabel = "***"; |
| 88 | + // get label of destination addr, if instr transfers control |
| 89 | + if (instr.r == Machine.Reg.CB.ordinal()) |
| 90 | + targetLabel = addrToLabel.get(instr.d); |
| 91 | + |
| 92 | + Machine.Op instruction = Machine.intToOp[instr.op]; |
| 93 | + asmWrite(String.format("%-7s",instruction.toString())); |
| 94 | + switch (instruction) { |
| 95 | + case LOAD: |
| 96 | + blankN(); |
| 97 | + writeD(instr.d); |
| 98 | + writeR('[', instr.r, ']'); |
| 99 | + break; |
| 100 | + |
| 101 | + case LOADA: |
| 102 | + blankN(); |
| 103 | + writeD(instr.d); |
| 104 | + writeR('[', instr.r, ']'); |
| 105 | + break; |
| 106 | + |
| 107 | + case LOADI: |
| 108 | + break; |
| 109 | + |
| 110 | + case LOADL: |
| 111 | + blankN(); |
| 112 | + writeD(instr.d); |
| 113 | + break; |
| 114 | + |
| 115 | + case STORE: |
| 116 | + blankN(); |
| 117 | + writeD(instr.d); |
| 118 | + writeR('[', instr.r, ']'); |
| 119 | + break; |
| 120 | + |
| 121 | + case STOREI: |
| 122 | + break; |
| 123 | + |
| 124 | + case CALL: |
| 125 | + if (instr.r == Machine.Reg.PB.ordinal()) { |
| 126 | + blankN(); |
| 127 | + writePrimitive(instr.d); |
| 128 | + } else { |
| 129 | + blankN(); |
| 130 | + asmWrite(targetLabel); |
| 131 | + } |
| 132 | + break; |
| 133 | + |
| 134 | + case CALLI: |
| 135 | + blankN(); |
| 136 | + asmWrite(targetLabel); |
| 137 | + break; |
| 138 | + |
| 139 | + case RETURN: |
| 140 | + writeN(instr.n); |
| 141 | + writeD(instr.d); |
| 142 | + break; |
| 143 | + |
| 144 | + case CALLD: |
| 145 | + blankN(); |
| 146 | + writeD(instr.d); |
| 147 | + break; |
| 148 | + |
| 149 | + case PUSH: |
| 150 | + blankN(); |
| 151 | + writeD(instr.d); |
| 152 | + break; |
| 153 | + |
| 154 | + case POP: |
| 155 | + blankN(); |
| 156 | + writeD(instr.d); |
| 157 | + break; |
| 158 | + |
| 159 | + case JUMP: |
| 160 | + blankN(); |
| 161 | + asmWrite(targetLabel); |
| 162 | + break; |
| 163 | + |
| 164 | + case JUMPI: |
| 165 | + break; |
| 166 | + |
| 167 | + case JUMPIF: |
| 168 | + writeN(instr.n); |
| 169 | + asmWrite(targetLabel); |
| 170 | + break; |
| 171 | + |
| 172 | + case HALT: |
| 173 | + writeN(instr.n); |
| 174 | + break; |
| 175 | + |
| 176 | + default: |
| 177 | + asmWrite("???? "); |
| 178 | + writeN(instr.n); |
| 179 | + writeD(instr.d); |
| 180 | + writeR('[', instr.r, ']'); |
| 181 | + break; |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + /** |
| 186 | + * disassembles program held in code store |
| 187 | + */ |
| 188 | + void disassembleProgram(String asmFileName) { |
| 189 | + |
| 190 | + try { |
| 191 | + asmOut = new FileWriter(asmFileName); |
| 192 | + } catch (IOException e) { |
| 193 | + System.out.println("Disassembler: can not create asm output file " |
| 194 | + + asmName); |
| 195 | + error = true; |
| 196 | + return; |
| 197 | + } |
| 198 | + |
| 199 | + // collect all addresses that may be the target of a jump instruction |
| 200 | + SortedSet<Integer> targets = new TreeSet<Integer>(); |
| 201 | + for (int addr = Machine.CB; addr < Machine.CT; addr++) { |
| 202 | + Instruction inst = Machine.code[addr]; |
| 203 | + Machine.Op op = Machine.intToOp[inst.op]; |
| 204 | + switch (op) { |
| 205 | + case CALL: |
| 206 | + case CALLI: |
| 207 | + // only consider calls (branches) within code memory (i.e. not primitives) |
| 208 | + if (inst.r == Machine.Reg.CB.ordinal()) |
| 209 | + targets.add(inst.d); |
| 210 | + break; |
| 211 | + case JUMP: |
| 212 | + // address following an unconditional branch is an implicit target |
| 213 | + targets.add(addr+1); |
| 214 | + targets.add(inst.d); |
| 215 | + break; |
| 216 | + case JUMPIF: |
| 217 | + // a jump of any sort creates a branch target |
| 218 | + targets.add(inst.d); |
| 219 | + break; |
| 220 | + default: |
| 221 | + break; |
| 222 | + } |
| 223 | + } |
| 224 | + |
| 225 | + // map branch target addresses to unique labels |
| 226 | + addrToLabel = new HashMap<Integer, String>(); |
| 227 | + int labelCounter = 10; |
| 228 | + for (Integer addr : targets) { |
| 229 | + String label = "L" + labelCounter++ ; |
| 230 | + addrToLabel.put(addr, label); |
| 231 | + } |
| 232 | + |
| 233 | + // disassemble each instruction |
| 234 | + for (int addr = Machine.CB; addr < Machine.CT; addr++) { |
| 235 | + |
| 236 | + // generate instruction address |
| 237 | + asmWrite(String.format("%3d ", addr)); |
| 238 | + |
| 239 | + // if this addr is a branch target, output label |
| 240 | + if (addrToLabel.containsKey(addr)) |
| 241 | + asmWrite(String.format("%-7s", addrToLabel.get(addr) + ":")); |
| 242 | + else |
| 243 | + asmWrite(" "); |
| 244 | + |
| 245 | + // instruction |
| 246 | + writeInstruction(Machine.code[addr]); |
| 247 | + |
| 248 | + // newline |
| 249 | + asmWrite("\n"); |
| 250 | + } |
| 251 | + |
| 252 | + // close output file |
| 253 | + try { |
| 254 | + asmOut.close(); |
| 255 | + } catch (IOException e) { |
| 256 | + error = true; |
| 257 | + } |
| 258 | + } |
| 259 | + |
| 260 | + private void asmWrite(String s) { |
| 261 | + try { |
| 262 | + asmOut.write(s); |
| 263 | + } catch (IOException e) { |
| 264 | + error = true; |
| 265 | + } |
| 266 | + } |
| 267 | + |
| 268 | + public static void main(String[] args) { |
| 269 | + System.out.println("********** mJAM Disassembler (1.0) **********"); |
| 270 | + String objectFileName = "obj.mJAM"; |
| 271 | + if (args.length == 1) |
| 272 | + objectFileName = args[0]; |
| 273 | + Disassembler d = new Disassembler(objectFileName); |
| 274 | + d.disassemble(); |
| 275 | + } |
| 276 | + |
| 277 | + /** |
| 278 | + * Disassemble object file |
| 279 | + * @return true if error encountered else false |
| 280 | + */ |
| 281 | + public boolean disassemble() { |
| 282 | + ObjectFile objectFile = new ObjectFile(objectFileName); |
| 283 | + |
| 284 | + // read object file into code store |
| 285 | + if (objectFile.read()) { |
| 286 | + System.out.println("Disassembler: unable to read object file" |
| 287 | + + objectFileName); |
| 288 | + return true; |
| 289 | + } |
| 290 | + |
| 291 | + // assembler-code output file name |
| 292 | + if (objectFileName.endsWith(".mJAM")) |
| 293 | + asmName = objectFileName.substring(0, objectFileName.length() - 5) |
| 294 | + + ".asm"; |
| 295 | + else |
| 296 | + asmName = objectFileName + ".asm"; |
| 297 | + |
| 298 | + // disassemble to file |
| 299 | + disassembleProgram(asmName); |
| 300 | + |
| 301 | + if (error) { |
| 302 | + System.out.println("Disassembler: unable to write asm file" |
| 303 | + + asmName); |
| 304 | + return true; |
| 305 | + } |
| 306 | + |
| 307 | + return false; |
| 308 | + } |
| 309 | +} |
0 commit comments