/* Using any language, implement an assembler + simulator for a new assembly language that has the following characteristics: - Maintains a set of registers (can be thought of as variables) that store integers and can be used to move and process data via instructions - Instructions are executed sequentially - Instructions should support constant values for arguments in addition to registers here are some example instructions: add 45 5 ra # add 45 + 5, store result in ra add 10 rb rb # add 10 to rb, store in rb sub ra rb rc # subtract ra (50) - rb (10) , store the result (40) in rc The assembler should take a newline separated string of instructions, which then get parsed and executed by the simulator. Feel free to implement whatever sort of abstractions make the most sense to you, but try to design for extensibility. For this entire exercise you can assume that the given input will always be correctly formatted for your implementation. =================== As a first step, we will be implementing basic arithmetic functionality for our language. To start with, we will implement the add and subtract instructions mentioned in the earlier example, as well as instructions for multiplication and division. add 45 5 ra add 10 rb rb sub ra rb rc mul ra rc rd div rd rb re Registers that are not initialized should be treated as 0, therefore the final register state should be: ra = 50, rb = 10, rc = 40, rd = 2000, re = 200 This first pass should take in a single string that contains multiple instruction statements separated by newline characters: add 45 5 ra\nadd 10 rb rb\nsub ra rb rc\nmul ra rc rd\ndiv rd rb re ============== Now that we have a couple working instructions, it’s time to implement process memory. Most processors have a small, finite amount of pre-defined registers, so they need a larger but slower store for larger sets of data. With that in mind, memory should be some indexable data structure, with each offset capable of storing the full contents of a register. For example, we might want to set memory[55] = 10. To interact with memory, we will need to add two new instruction operand types to our language: add 0 8 ra # store 8 into ra add ra 5 [ra] # add 5 to ra (8), store result in memory[ra] (ra is still 8, so mem[8] = 13); memory[ra] = ra + 5 sub [ra] 10 [8] # load memory[ra] and subtract 10 from it, store the result back into memory[8]; memory[8] = memory[ra] - 10 add [ra] [8] rb # should result in rb = 3 + 3 The final register state should show ra = 8, rb = 6, and memory should show memory[8] = 3. We will use this new example as our new test string. Here is our raw input: add 0 8 ra\nadd ra 5 [ra]\nsub [ra] 10 [8]\nadd [ra] [8] rb */ import java.io.*; import java.util.*; // interface instruction { // abstract void execute(String arg1, String arg2, String arg3); // } // class add implements instruction { // Map registers; // add() { // registers = new HashMap<>(); // } // public void execute(String arg1, String arg2, String arg3) { // return; // } // } // class mul implements instruction { // Map registers; // mul() { // registers = new HashMap<>(); // } // public void execute(String arg1, String arg2, String arg3) { // return; // } // } class Solution { int[] memory = new int[100]; public void main() { // String inputString = "add 45 5 ra\nadd 10 rb rb\nsub ra rb rc\nmul ra rc rd\ndiv rd rb re"; String inputString = "add 0 8 ra\nadd ra 5 [ra]\nsub [ra] 10 [8]\nadd [ra] [8] rb"; Map registers = new HashMap<>(); for (String r : List.of("ra", "rb", "rc", "rd", "re")) { registers.put(r, 0); } for(String instruction : inputString.split("\n")) { String[] instructAsArr = instruction.split(" "); int a = getArgAsInt(instructAsArr[1], registers); int b = getArgAsInt(instructAsArr[2], registers); int output =0; boolean isMemory = false; if (instructAsArr[3].charAt(0) == '[') { instructAsArr[3].substring(1, instructAsArr[3].length()); int memoryAddr = registers.containsKey(instructAsArr[3]) ? registers.get(instructAsArr[3]) : Integer.parseInt(instructAsArr[3]); isMemory = true; output = memoryAddr; } switch(instructAsArr[0]) { case "add": if (isMemory) { memory[output] = a+b; } else { registers.put(instructAsArr[3], a+b); } break; case "sub": if (isMemory) { memory[output] = a-b; } else { registers.put(instructAsArr[3], a-b); } break; case "mul": if (isMemory) { memory[output] = a*b; } else { registers.put(instructAsArr[3], a*b); } break; case "div": if (isMemory) { memory[output] = a/b; } else { registers.put(instructAsArr[3], a/b); } break; default: // throw new Exception("Unsupported operation"); } } for (Map.Entry element : registers.entrySet()) { System.out.println(element.getKey() + ": " + element.getValue()); } } private int getArgAsInt(String arg, Map registers) { if (arg.charAt(0) == '[') { arg.substring(1, arg.length()); int memoryAddr = registers.containsKey(arg) ? registers.get(arg) : Integer.parseInt(arg); return memory[memoryAddr]; } return registers.containsKey(arg) ? registers.get(arg) : Integer.parseInt(arg); } }