Assembler.java

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
Location : assemble
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:assembleWithoutAnExplicitStartAddressDefaultsToZero()]
replaced return value with null for com/rox/cpu/mos6502/assembler/Assembler::assemble → KILLED

32

1.1
Location : assemble
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:duplicateLabelThrows()]
negated conditional → KILLED

33

1.1
Location : assemble
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:duplicateLabelThrows()]
removed call to com/rox/cpu/mos6502/assembler/SymbolTable::define → KILLED

36

1.1
Location : assemble
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:undefinedLabelReferenceThrows()]
negated conditional → KILLED

45

1.1
Location : assemble
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:forwardBranchResolvesCorrectly()]
Replaced integer addition with subtraction → KILLED

2.2
Location : assemble
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:forwardBranchResolvesCorrectly()]
Replaced integer addition with subtraction → KILLED

50

1.1
Location : assemble
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:undefinedLabelReferenceThrows()]
removed call to com/rox/cpu/mos6502/assembler/Assembler::emit → KILLED

53

1.1
Location : assemble
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:assembleWithoutAnExplicitStartAddressDefaultsToZero()]
replaced return value with null for com/rox/cpu/mos6502/assembler/Assembler::assemble → KILLED

60

1.1
Location : emit
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:assembleWithoutAnExplicitStartAddressDefaultsToZero()]
negated conditional → KILLED

63

1.1
Location : emit
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:forwardBranchResolvesCorrectly()]
negated conditional → KILLED

64

1.1
Location : emit
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:forwardBranchResolvesCorrectly()]
removed call to com/rox/cpu/mos6502/assembler/Assembler::appendRelativeOffset → KILLED

66

1.1
Location : emit
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:jmpToALabelEmitsALittleEndianAbsoluteAddress()]
removed call to com/rox/cpu/mos6502/assembler/Assembler::appendOperandBytes → KILLED

71

1.1
Location : emit
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:assemblesAStraightLineProgramWithNoLabelReferences()]
removed call to com/rox/cpu/mos6502/assembler/Assembler::appendOperandBytes → KILLED

75

1.1
Location : appendRelativeOffset
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:forwardBranchResolvesCorrectly()]
Replaced integer addition with subtraction → KILLED

2.2
Location : appendRelativeOffset
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:forwardBranchResolvesCorrectly()]
Replaced integer subtraction with addition → KILLED

77

1.1
Location : appendRelativeOffset
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:branchOffsetOfExactlyNegative128Succeeds()]
changed conditional boundary → KILLED

2.2
Location : appendRelativeOffset
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:branchOffsetOfExactlyPositive127Succeeds()]
changed conditional boundary → KILLED

3.3
Location : appendRelativeOffset
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:forwardBranchResolvesCorrectly()]
negated conditional → KILLED

4.4
Location : appendRelativeOffset
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:forwardBranchResolvesCorrectly()]
negated conditional → KILLED

81

1.1
Location : appendRelativeOffset
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:forwardBranchResolvesCorrectly()]
Replaced bitwise AND with OR → KILLED

86

1.1
Location : appendOperandBytes
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:labelOnlyLineFollowedByLabelAndInstructionLineBothResolveToTheSameAddress()]
Replaced bitwise AND with OR → KILLED

88

1.1
Location : appendOperandBytes
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:jmpToALabelEmitsALittleEndianAbsoluteAddress()]
Replaced bitwise AND with OR → KILLED

89

1.1
Location : appendOperandBytes
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:jmpToALabelEmitsALittleEndianAbsoluteAddress()]
Replaced Shift Right with Shift Left → KILLED

2.2
Location : appendOperandBytes
Killed by : com.rox.cpu.mos6502.assembler.AssemblerTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.AssemblerTest]/[method:jmpToALabelEmitsALittleEndianAbsoluteAddress()]
Replaced bitwise AND with OR → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1