| 1 | package com.rox.cpu.mos6502.assembler; | |
| 2 | ||
| 3 | import com.rox.cpu.mos6502.MOS6502OpCode; | |
| 4 | ||
| 5 | import java.util.ArrayList; | |
| 6 | import java.util.List; | |
| 7 | ||
| 8 | /** | |
| 9 | * Assembles 6502 text source into bytes loadable into memory. | |
| 10 | * <p> | |
| 11 | * Two passes: pass 1 walks the source, defines labels against their address, and resolves each | |
| 12 | * instruction's mnemonic + operand to a concrete opcode. This never needs a label's actual value - | |
| 13 | * label operands resolve to RELATIVE or ABSOLUTE purely from what the mnemonic supports, so | |
| 14 | * instruction length - and therefore every address - is fully known by the end of pass 1. Pass 2 | |
| 15 | * emits bytes, resolving label references against the now-complete {@link SymbolTable}. | |
| 16 | */ | |
| 17 | public final class Assembler { | |
| 18 | ||
| 19 | private Assembler() { | |
| 20 | } | |
| 21 | ||
| 22 | public static AssembledProgram assemble(final String source) { | |
| 23 |
1
1. assemble : replaced return value with null for com/rox/cpu/mos6502/assembler/Assembler::assemble → KILLED |
return assemble(source, 0); |
| 24 | } | |
| 25 | ||
| 26 | public static AssembledProgram assemble(final String source, final int startAddress) { | |
| 27 | final SymbolTable symbolTable = new SymbolTable(); | |
| 28 | final List<PendingInstruction> pendingInstructions = new ArrayList<>(); | |
| 29 | int address = startAddress; | |
| 30 | ||
| 31 | for (final SourceLine line : LineScanner.scan(source)) { | |
| 32 |
1
1. assemble : negated conditional → KILLED |
if (line.isLabelled()) { |
| 33 |
1
1. assemble : removed call to com/rox/cpu/mos6502/assembler/SymbolTable::define → KILLED |
symbolTable.define(line.label(), address, line.lineNumber()); |
| 34 | } | |
| 35 | ||
| 36 |
1
1. assemble : negated conditional → KILLED |
if (!line.containsCode()) { |
| 37 | continue; | |
| 38 | } | |
| 39 | ||
| 40 | final Operand operand = OperandParser.parse(line.lineNumber(), line.operandText()); | |
| 41 | final MOS6502OpCode opcode = InstructionResolver.resolve(line.mnemonic(), operand, line.lineNumber()); | |
| 42 | ||
| 43 | pendingInstructions.add(new PendingInstruction(address, opcode, operand, line.lineNumber())); | |
| 44 | ||
| 45 |
2
1. assemble : Replaced integer addition with subtraction → KILLED 2. assemble : Replaced integer addition with subtraction → KILLED |
address += 1 + OpCodeTable.modeOf(opcode).operandByteCount(); |
| 46 | } | |
| 47 | ||
| 48 | final List<Integer> programAsBytes = new ArrayList<>(); | |
| 49 | for (final PendingInstruction instruction : pendingInstructions) { | |
| 50 |
1
1. assemble : removed call to com/rox/cpu/mos6502/assembler/Assembler::emit → KILLED |
emit(programAsBytes, instruction, symbolTable); |
| 51 | } | |
| 52 | ||
| 53 |
1
1. assemble : replaced return value with null for com/rox/cpu/mos6502/assembler/Assembler::assemble → KILLED |
return new AssembledProgram(startAddress, programAsBytes.stream().mapToInt(Integer::intValue).toArray(), symbolTable.asMap()); |
| 54 | } | |
| 55 | ||
| 56 | private static void emit(final List<Integer> programBytes, final PendingInstruction instruction, final SymbolTable symbolTable) { | |
| 57 | final AddressingMode mode = OpCodeTable.modeOf(instruction.opcode()); | |
| 58 | programBytes.add(instruction.opcode().getId()); | |
| 59 | ||
| 60 |
1
1. emit : negated conditional → KILLED |
if (instruction.operand().isLabelReference()) { |
| 61 | final int target = symbolTable.resolve(instruction.operand().label(), instruction.lineNumber()); | |
| 62 | ||
| 63 |
1
1. emit : negated conditional → KILLED |
if (mode == AddressingMode.RELATIVE) { |
| 64 |
1
1. emit : removed call to com/rox/cpu/mos6502/assembler/Assembler::appendRelativeOffset → KILLED |
appendRelativeOffset(programBytes, target, instruction.address(), instruction.lineNumber()); |
| 65 | } else { | |
| 66 |
1
1. emit : removed call to com/rox/cpu/mos6502/assembler/Assembler::appendOperandBytes → KILLED |
appendOperandBytes(programBytes, mode, target); |
| 67 | } | |
| 68 | return; | |
| 69 | } | |
| 70 | ||
| 71 |
1
1. emit : removed call to com/rox/cpu/mos6502/assembler/Assembler::appendOperandBytes → KILLED |
appendOperandBytes(programBytes, mode, instruction.operand().value()); |
| 72 | } | |
| 73 | ||
| 74 | private static void appendRelativeOffset(final List<Integer> bytes, final int target, final int instructionAddress, final int lineNumber) { | |
| 75 |
2
1. appendRelativeOffset : Replaced integer addition with subtraction → KILLED 2. appendRelativeOffset : Replaced integer subtraction with addition → KILLED |
final int offset = target - (instructionAddress + 2); |
| 76 | ||
| 77 |
4
1. appendRelativeOffset : changed conditional boundary → KILLED 2. appendRelativeOffset : changed conditional boundary → KILLED 3. appendRelativeOffset : negated conditional → KILLED 4. appendRelativeOffset : negated conditional → KILLED |
if (offset < -128 || offset > 127) { |
| 78 | throw new AssemblyException(lineNumber, "Branch target out of range: offset " + offset); | |
| 79 | } | |
| 80 | ||
| 81 |
1
1. appendRelativeOffset : Replaced bitwise AND with OR → KILLED |
bytes.add(offset & 0xFF); |
| 82 | } | |
| 83 | ||
| 84 | private static void appendOperandBytes(final List<Integer> programBytes, final AddressingMode mode, final int value) { | |
| 85 | switch (mode.operandByteCount()) { | |
| 86 |
1
1. appendOperandBytes : Replaced bitwise AND with OR → KILLED |
case 1 -> programBytes.add(value & 0xFF); |
| 87 | case 2 -> { | |
| 88 |
1
1. appendOperandBytes : Replaced bitwise AND with OR → KILLED |
programBytes.add(value & 0xFF); |
| 89 |
2
1. appendOperandBytes : Replaced Shift Right with Shift Left → KILLED 2. appendOperandBytes : Replaced bitwise AND with OR → KILLED |
programBytes.add((value >> 8) & 0xFF); |
| 90 | } | |
| 91 | default -> { | |
| 92 | // 0 operand bytes: IMPLIED / ACCUMULATOR | |
| 93 | } | |
| 94 | } | |
| 95 | } | |
| 96 | } | |
Mutations | ||
| 23 |
1.1 |
|
| 32 |
1.1 |
|
| 33 |
1.1 |
|
| 36 |
1.1 |
|
| 45 |
1.1 2.2 |
|
| 50 |
1.1 |
|
| 53 |
1.1 |
|
| 60 |
1.1 |
|
| 63 |
1.1 |
|
| 64 |
1.1 |
|
| 66 |
1.1 |
|
| 71 |
1.1 |
|
| 75 |
1.1 2.2 |
|
| 77 |
1.1 2.2 3.3 4.4 |
|
| 81 |
1.1 |
|
| 86 |
1.1 |
|
| 88 |
1.1 |
|
| 89 |
1.1 2.2 |