Assembler { public static Assembly patchLabels(Assembly assembly) { List<Instruction> updatedAssembly = new ArrayList<>(); Map<String, Long> updatedLabelMap = new HashMap<>(assembly.labelMap); int lineNumber = 0; for (Instruction instruction : assembly.instructions) { int patchOffset = 0; if (instruction.opcode.type == OpcodeType.INDIRECT) { if (instruction.opcode.encodesTo > 0xFF) { throw new IllegalArgumentException("Indirect instructions with opcodes larger than a byte are not supported."); } if (instruction.opcode.encodesTo > 0xF) { patchOffset++; } updatedAssembly.add(instruction); } else if (instruction.labelOperand != null) { patchOffset += 7; updatedAssembly.add(instruction); } else if (instruction.constantOperand != null) { if (instruction.constantOperand < 0 || instruction.constantOperand > 0xF) { List<Instruction> prefixResult = prefix(instruction); patchOffset += prefixResult.size() - 1; updatedAssembly.addAll(prefixResult); } else { updatedAssembly.add(instruction); } } else { throw new IllegalArgumentException("Instruction '" + instruction + "' does not have a valid operand."); } for (Map.Entry<String, Long> labelEntry : updatedLabelMap.entrySet()) { if (labelEntry.getValue() > lineNumber) { labelEntry.setValue(labelEntry.getValue() + patchOffset); } } lineNumber += patchOffset + 1; } return new Assembly(updatedAssembly, updatedLabelMap); } private  Assembler(); @Nullable static AssemblerConfig loadConfig(@Nullable Reader configReader); static List<String> assemble(@Nonnull String input, @Nullable AssemblerConfig config, @Nonnull PrintStream logger, boolean insertIOConfiguration); @Nonnull static List<Instruction> parseAssembly(@Nonnull String input); static List<Instruction> insertIOConfiguration(List<Instruction> instructions, AssemblerConfig config); static List<Instruction> processDollarLabelOperands(List<Instruction> instructions); static List<Instruction> processChanDirectives(List<Instruction> instructions, AssemblerConfig config); static List<Instruction> processDataDirectives(List<Instruction> instructions); static Assembly makeLabelMap(List<Instruction> instructions); static Map<String, Long> getConfigLabels(@Nonnull AssemblerConfig config); static Assembly patchLabels(Assembly assembly); static List<Byte> processIndirectInstruction(Instruction instruction); static List<Byte> processDirectInstructionWithLabelOperand(Instruction instruction, Map<String, Long> labelMap); static byte processDirectInstructionWithConstantOperand(Instruction instruction); static List<Instruction> prefix(Instruction instruction); static ParseTree parseExpression(String expression, Function<AssemblerExpressionParser, ParseTree> startRule); final static long maxInt; final static long minInt; final static Pattern inputComment; final static Pattern instructionPattern; final static Pattern directivePattern; final static Pattern dollarPattern; static Map<String, Opcode> opcodes; }
@Test public void testPatchLabels() { List<Instruction> instructions = Arrays.asList( new Instruction(InstructionType.INSTRUCTION, "init", Assembler.opcodes.get("ldc"), 89L, null, null, null, 1), new Instruction(InstructionType.INSTRUCTION, "space", Assembler.opcodes.get("#empty"), 0L, null, null, null, 2), new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("#empty"), 0L, null, null, null, 2), new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("#empty"), 0L, null, null, null, 2), new Instruction(InstructionType.INSTRUCTION, "~configuration", Assembler.opcodes.get("confio"), null, null, null, null, 3), new Instruction(InstructionType.INSTRUCTION, "alabel", Assembler.opcodes.get("ajw"), -10L, null, null, null, 4), new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("ldlp"), 0L, null, null, null, 5), new Instruction(InstructionType.INSTRUCTION, "jump", Assembler.opcodes.get("j"), null, "~configuration", null, null, 7), new Instruction(InstructionType.INSTRUCTION, "end", Assembler.opcodes.get("ldc"), 0L, null, null, null, 8) ); Map<String, Long> labelMap = new HashMap<>(); labelMap.put("init", 0L); labelMap.put("space", 1L); labelMap.put("~configuration", 4L); labelMap.put("alabel", 5L); labelMap.put("jump", 7L); labelMap.put("end", 8L); Assembly patchedAssembly = Assembler.patchLabels(new Assembly(instructions, labelMap)); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "init", Assembler.opcodes.get("pfix"), 5L, null, null, null, 1), patchedAssembly.instructions.get(0) ); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("ldc"), 9L, null, null, null, 1), patchedAssembly.instructions.get(1) ); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "space", Assembler.opcodes.get("#empty"), 0L, null, null, null, 2), patchedAssembly.instructions.get(2) ); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("#empty"), 0L, null, null, null, 2), patchedAssembly.instructions.get(3) ); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("#empty"), 0L, null, null, null, 2), patchedAssembly.instructions.get(4) ); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "~configuration", Assembler.opcodes.get("confio"), null, null, null, null, 3), patchedAssembly.instructions.get(5) ); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "alabel", Assembler.opcodes.get("nfix"), 0L, null, null, null, 4), patchedAssembly.instructions.get(6) ); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("ajw"), 6L, null, null, null, 4), patchedAssembly.instructions.get(7) ); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, null, Assembler.opcodes.get("ldlp"), 0L, null, null, null, 5), patchedAssembly.instructions.get(8) ); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "jump", Assembler.opcodes.get("j"), null, "~configuration", null, null, 7), patchedAssembly.instructions.get(9) ); checkInstructionEquality( new Instruction(InstructionType.INSTRUCTION, "end", Assembler.opcodes.get("ldc"), 0L, null, null, null, 8), patchedAssembly.instructions.get(10) ); assertEquals(new Long(0), patchedAssembly.labelMap.get("init")); assertEquals(new Long(2), patchedAssembly.labelMap.get("space")); assertEquals(new Long(5), patchedAssembly.labelMap.get("~configuration")); assertEquals(new Long(6), patchedAssembly.labelMap.get("alabel")); assertEquals(new Long(9), patchedAssembly.labelMap.get("jump")); assertEquals(new Long(17), patchedAssembly.labelMap.get("end")); }