package swen90006.machine; import java.util.Arrays; import java.util.List; public class Machine { /** arithmetic instructions each take three registers as arguments with the * destination register appearing first */ /** add rd rs1 rs2 =~ rd = rs1 + rs2 */ public static final String INSTRUCTION_ADD = "add"; /** sub rd rs1 rs2 =~ rd = rs1 - rs2 */ public static final String INSTRUCTION_SUBTRACT = "sub"; /** mul rd rs1 rs2 =~ rd = rs1 * rs2 */ public static final String INSTRUCTION_MULT = "mul"; /** div rd rs1 rs2 =~ rd = rs1 / rs2 */ public static final String INSTRUCTION_DIVIDE = "div"; /** ret rs =~ return rs */ public static final String INSTRUCTION_RETURN = "ret"; /** ldr rd rs offs =~ rd = rs[offs] */ public static final String INSTRUCTION_LOAD = "ldr"; /** str ra offs rb =~ ra[offs] = rb */ public static final String INSTRUCTION_STORE = "str"; /** mov rd val =~ rd = val */ public static final String INSTRUCTION_MOVE = "mov"; /** jmp offs =~ pc = pc + offs */ public static final String INSTRUCTION_JUMP = "jmp"; /** jz ra offs =~ if (ra == 0) pc = pc + offs else pc = pc + 1 */ public static final String INSTRUCTION_JZ = "jz"; public static final int NUM_REGS = 32; public static final int MAX_REG = (NUM_REGS - 1); public static final int MEMORY_SIZE = 65536; /* 4 x as much memory as a 64 */ public static final int MAX_ADDR = MEMORY_SIZE-1; private int[] memory; private int[] regs; private int count = 0; /* counts number of instructions executed so far */ public Machine() { memory = new int[MEMORY_SIZE]; regs = new int[NUM_REGS]; count = 0; } private void do_add(int dest, int src1, int src2) { regs[dest] = regs[src1] + regs[src2]; } private void do_sub(int dest, int src1, int src2) { regs[dest] = regs[src1] - regs[src2]; } private void do_mult(int dest, int src1, int src2) { regs[dest] = regs[src1] * regs[src2]; } private void do_div(int dest, int src1, int src2) { if (regs[src2] == 0){ /* no op */ }else{ regs[dest] = regs[src1] / regs[src2]; } } private void do_load(int dest, int src, int offs) { if (regs[src] + offs > MAX_ADDR){ /* no op */ }else if(regs[src] + offs < 0){ /* no op */ }else{ regs[dest] = memory[regs[src] + offs]; } } private void do_store(int a, int offs, int b) { if (regs[a] + offs > MAX_ADDR){ /* no op */ }else if(regs[a] + offs < 0){ /* no op */ }else{ memory[regs[a] + offs] = regs[b]; } } private void do_move(int rd, int val){ regs[rd] = val; } private int parseReg(String s) throws InvalidInstructionException { if (s.length() < 2){ throw new InvalidInstructionException(); } if (s.charAt(0) != 'r'){ throw new InvalidInstructionException(); } String numstr = s.substring(1); int num = 0; try { num = Integer.parseInt(numstr); } catch (Exception e){ throw new InvalidInstructionException(); } validate_reg(num); return num; } private int parseOffset(String s) throws InvalidInstructionException { int num = 0; try { num = Integer.parseInt(s); } catch (Exception e){ throw new InvalidInstructionException(); } validate_offset(num); return num; } private void validate_reg(int reg) throws InvalidInstructionException { if (reg < 0 || reg > MAX_REG) { throw new InvalidInstructionException(); } } private void validate_offset(int offset) throws InvalidInstructionException { if (offset < -MAX_ADDR || offset > MAX_ADDR) { throw new InvalidInstructionException(); } } /** Execute an assembly program. * * @param prog is the program to execute as an iterable collection of strings, * each of which is a single instruction. * @return the program's return value. * @throws Exception when program has unrecognised or * invalid instructions, or when it returns no result when it finishes */ int execute(List<String> instructions) throws InvalidInstructionException, NoReturnValueException { int instructionsExecuted = 0; int pc = 0; final int progLength = instructions.size(); while(true){ if (pc < 0 || pc >= progLength){ /* will cause NoReturnValueException to be thrown * but that is not a bug and and indeed is what the * VM is supposed to do if the pc becomes negative, * since in this case the program's execution * finishes early without a return value having * been produced. */ break; } String inst = instructions.get(pc); /* strip leading and trailing whitespace */ inst = inst.toLowerCase().replaceAll("^\\s+","").replaceAll("\\s+$",""); /* strip out any comments */ String[] toks = inst.split(";"); inst = toks[0]; /* check for blank lines */ if (inst.equals("")){ pc = pc + 1; count++; continue; } instructionsExecuted++; /* now tokenize by splitting on whitespace */ toks = inst.split("\\s+"); /* check minimum number of tokens */ if (toks.length < 2){ throw new InvalidInstructionException(); } if (toks[0].equals(INSTRUCTION_ADD)){ if (toks.length != 4){ throw new InvalidInstructionException(); } int rd = parseReg(toks[1]); int rs1 = parseReg(toks[2]); int rs2 = parseReg(toks[3]); do_add(rd,rs1,rs2); } else if (toks[0].equals(INSTRUCTION_SUBTRACT)){ if (toks.length != 4){ throw new InvalidInstructionException(); } int rd = parseReg(toks[1]); int rs1 = parseReg(toks[2]); int rs2 = parseReg(toks[3]); do_sub(rd,rs1,rs2); } else if (toks[0].equals(INSTRUCTION_MULT)){ if (toks.length != 4){ throw new InvalidInstructionException(); } int rd = parseReg(toks[1]); int rs1 = parseReg(toks[2]); int rs2 = parseReg(toks[3]); do_mult(rd,rs1,rs2); } else if (toks[0].equals(INSTRUCTION_DIVIDE)){ if (toks.length != 4){ throw new InvalidInstructionException(); } int rd = parseReg(toks[1]); int rs1 = parseReg(toks[2]); int rs2 = parseReg(toks[3]); do_div(rd,rs1,rs2); } else if (toks[0].equals(INSTRUCTION_RETURN)){ int rs = parseReg(toks[1]); count++; return regs[rs]; } else if (toks[0].equals(INSTRUCTION_LOAD)){ if (toks.length != 4){ throw new InvalidInstructionException(); } int rd = parseReg(toks[1]); int rs = parseReg(toks[2]); int offs = parseOffset(toks[3]); do_load(rd,rs,offs); } else if (toks[0].equals(INSTRUCTION_STORE)){ if (toks.length != 4){ throw new InvalidInstructionException(); } int ra = parseReg(toks[1]); int offs = parseOffset(toks[2]); int rb = parseReg(toks[3]); do_store(ra,offs,rb); } else if (toks[0].equals(INSTRUCTION_MOVE)){ if (toks.length != 3){ throw new InvalidInstructionException(); } int rd = parseReg(toks[1]); int offs = parseOffset(toks[2]); do_move(rd,offs); } else if (toks[0].equals(INSTRUCTION_JUMP)){ if (toks.length != 2){ throw new InvalidInstructionException(); } int offs = parseOffset(toks[1]); pc = pc + offs; count++; continue; /* avoid default increment of pc below */ } else if (toks[0].equals(INSTRUCTION_JZ)){ if (toks.length != 3){ throw new InvalidInstructionException(); } int ra = parseReg(toks[1]); int offs = parseOffset(toks[2]); if (regs[ra] == 0){ pc = pc + offs; } else{ pc = pc + 1; } count++; continue; /* avoid default increment the pc below */ } else { System.err.println("Unrecognised instruction: " + inst); throw new InvalidInstructionException(); } count++; pc = pc + 1; } /* got here without returning already... */ throw new NoReturnValueException(); } /** * get the number of instructions successfully executed by the VM so far */ public int getCount(){ return count; } }