InstructionResolver.java

1
package com.rox.cpu.mos6502.assembler;
2
3
import com.rox.cpu.mos6502.MOS6502OpCode;
4
5
/**
6
 * Resolves a mnemonic + {@link Operand} to a concrete {@link MOS6502OpCode}, disambiguating the
7
 * two sentinel {@link Operand} cases ({@link Operand#isEmpty()}, {@link Operand#isLabelReference()})
8
 * against whichever addressing modes the mnemonic actually supports, and falling back from a zero-page
9
 * mode to its absolute-family equivalent when the mnemonic has no zero-page variant.
10
 */
11
final class InstructionResolver {
12
13
    private InstructionResolver() {
14
    }
15
16
    static MOS6502OpCode resolve(final String mnemonic, final Operand operand, final int lineNumber) {
17 1 1. resolve : negated conditional → KILLED
        if (OpCodeTable.supportedModes(mnemonic).isEmpty()) {
18
            throw new AssemblyException(lineNumber, "Unknown mnemonic: " + mnemonic);
19
        }
20
21 1 1. resolve : negated conditional → KILLED
        if (operand.isEmpty()) {
22 1 1. resolve : replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolve → KILLED
            return resolveNoOperand(mnemonic, lineNumber);
23
        }
24
25 1 1. resolve : negated conditional → KILLED
        if (operand.isLabelReference()) {
26 1 1. resolve : replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolve → KILLED
            return resolveLabelReference(mnemonic, lineNumber);
27
        }
28
29 1 1. resolve : replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolve → KILLED
        return resolveDirect(mnemonic, operand.mode(), lineNumber);
30
    }
31
32
    private static MOS6502OpCode resolveNoOperand(final String mnemonic, final int lineNumber) {
33 1 1. resolveNoOperand : negated conditional → KILLED
        if (OpCodeTable.supports(mnemonic, AddressingMode.ACCUMULATOR)) {
34 1 1. resolveNoOperand : replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolveNoOperand → KILLED
            return OpCodeTable.resolve(mnemonic, AddressingMode.ACCUMULATOR);
35
        }
36 1 1. resolveNoOperand : negated conditional → KILLED
        if (OpCodeTable.supports(mnemonic, AddressingMode.IMPLIED)) {
37 1 1. resolveNoOperand : replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolveNoOperand → KILLED
            return OpCodeTable.resolve(mnemonic, AddressingMode.IMPLIED);
38
        }
39
        throw new AssemblyException(lineNumber, mnemonic + " requires an operand");
40
    }
41
42
    private static MOS6502OpCode resolveLabelReference(final String mnemonic, final int lineNumber) {
43 1 1. resolveLabelReference : negated conditional → KILLED
        if (OpCodeTable.supports(mnemonic, AddressingMode.RELATIVE)) {
44 1 1. resolveLabelReference : replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolveLabelReference → KILLED
            return OpCodeTable.resolve(mnemonic, AddressingMode.RELATIVE);
45
        }
46 1 1. resolveLabelReference : negated conditional → KILLED
        if (OpCodeTable.supports(mnemonic, AddressingMode.ABSOLUTE)) {
47 1 1. resolveLabelReference : replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolveLabelReference → KILLED
            return OpCodeTable.resolve(mnemonic, AddressingMode.ABSOLUTE);
48
        }
49
        throw new AssemblyException(lineNumber, mnemonic + " cannot take a label operand");
50
    }
51
52
    private static MOS6502OpCode resolveDirect(final String mnemonic, final AddressingMode mode, final int lineNumber) {
53 1 1. resolveDirect : negated conditional → KILLED
        if (OpCodeTable.supports(mnemonic, mode)) {
54 1 1. resolveDirect : replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolveDirect → KILLED
            return OpCodeTable.resolve(mnemonic, mode);
55
        }
56
57
        final AddressingMode widened = widenZeroPageToAbsolute(mode);
58 2 1. resolveDirect : negated conditional → KILLED
2. resolveDirect : negated conditional → KILLED
        if (widened != null && OpCodeTable.supports(mnemonic, widened)) {
59 1 1. resolveDirect : replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolveDirect → KILLED
            return OpCodeTable.resolve(mnemonic, widened);
60
        }
61
62
        throw new AssemblyException(lineNumber, mnemonic + " does not support " + mode
63
                + " addressing (supported: " + OpCodeTable.supportedModes(mnemonic) + ")");
64
    }
65
66
    private static AddressingMode widenZeroPageToAbsolute(final AddressingMode mode) {
67
        return switch (mode) {
68
            case ZERO_PAGE -> AddressingMode.ABSOLUTE;
69
            // ZERO_PAGE_X -> ABSOLUTE_X is unreachable for the current official opcode set: every
70
            // mnemonic with an ABSOLUTE_X form also has a ZERO_PAGE_X form, unlike ABSOLUTE_Y (below),
71
            // whose mnemonics never have a ZERO_PAGE_Y form. Re-enable if that ever stops holding -
72
            // e.g. a future opcode adds ABSOLUTE_X support without a matching ZERO_PAGE_X.
73
            // case ZERO_PAGE_X -> AddressingMode.ABSOLUTE_X;
74
            case ZERO_PAGE_Y -> AddressingMode.ABSOLUTE_Y;
75
            default -> null;
76
        };
77
    }
78
}

Mutations

17

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

21

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

22

1.1
Location : resolve
Killed by : com.rox.cpu.mos6502.assembler.InstructionResolverTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.InstructionResolverTest]/[method:emptyOperandResolvesToAccumulatorWhenTheMnemonicSupportsIt()]
replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolve → KILLED

25

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

26

1.1
Location : resolve
Killed by : com.rox.cpu.mos6502.assembler.InstructionResolverTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.InstructionResolverTest]/[method:labelOperandResolvesToRelativeForBranches()]
replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolve → KILLED

29

1.1
Location : resolve
Killed by : com.rox.cpu.mos6502.assembler.InstructionResolverTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.InstructionResolverTest]/[method:directModeResolvesToTheMatchingOpcode()]
replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolve → KILLED

33

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

34

1.1
Location : resolveNoOperand
Killed by : com.rox.cpu.mos6502.assembler.InstructionResolverTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.InstructionResolverTest]/[method:emptyOperandResolvesToAccumulatorWhenTheMnemonicSupportsIt()]
replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolveNoOperand → KILLED

36

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

37

1.1
Location : resolveNoOperand
Killed by : com.rox.cpu.mos6502.assembler.InstructionResolverTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.InstructionResolverTest]/[method:emptyOperandResolvesToImpliedWhenTheMnemonicHasNoAccumulatorVariant()]
replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolveNoOperand → KILLED

43

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

44

1.1
Location : resolveLabelReference
Killed by : com.rox.cpu.mos6502.assembler.InstructionResolverTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.InstructionResolverTest]/[method:labelOperandResolvesToRelativeForBranches()]
replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolveLabelReference → KILLED

46

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

47

1.1
Location : resolveLabelReference
Killed by : com.rox.cpu.mos6502.assembler.InstructionResolverTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.InstructionResolverTest]/[method:labelOperandResolvesToAbsoluteForJumpsAndCalls()]
replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolveLabelReference → KILLED

53

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

54

1.1
Location : resolveDirect
Killed by : com.rox.cpu.mos6502.assembler.InstructionResolverTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.InstructionResolverTest]/[method:directModeResolvesToTheMatchingOpcode()]
replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolveDirect → KILLED

58

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

2.2
Location : resolveDirect
Killed by : com.rox.cpu.mos6502.assembler.InstructionResolverTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.InstructionResolverTest]/[method:zeroPageModeWidensToAbsoluteWhenTheMnemonicHasNoZeroPageVariant()]
negated conditional → KILLED

59

1.1
Location : resolveDirect
Killed by : com.rox.cpu.mos6502.assembler.InstructionResolverTest.[engine:junit-jupiter]/[class:com.rox.cpu.mos6502.assembler.InstructionResolverTest]/[method:zeroPageModeWidensToAbsoluteWhenTheMnemonicHasNoZeroPageVariant()]
replaced return value with null for com/rox/cpu/mos6502/assembler/InstructionResolver::resolveDirect → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1