| 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 |
|
| 21 |
1.1 |
|
| 22 |
1.1 |
|
| 25 |
1.1 |
|
| 26 |
1.1 |
|
| 29 |
1.1 |
|
| 33 |
1.1 |
|
| 34 |
1.1 |
|
| 36 |
1.1 |
|
| 37 |
1.1 |
|
| 43 |
1.1 |
|
| 44 |
1.1 |
|
| 46 |
1.1 |
|
| 47 |
1.1 |
|
| 53 |
1.1 |
|
| 54 |
1.1 |
|
| 58 |
1.1 2.2 |
|
| 59 |
1.1 |