| 1 | package com.rox.cpu.mos6502.assembler; | |
| 2 | ||
| 3 | import com.rox.cpu.mos6502.MOS6502OpCode; | |
| 4 | ||
| 5 | import java.util.EnumMap; | |
| 6 | import java.util.HashMap; | |
| 7 | import java.util.Map; | |
| 8 | import java.util.Set; | |
| 9 | ||
| 10 | /** | |
| 11 | * A mnemonic + {@link AddressingMode} -> {@link MOS6502OpCode} lookup, built once from | |
| 12 | * {@link MOS6502OpCode#values()} by splitting each constant's name on its first {@code _} | |
| 13 | * (e.g. {@code LDA_ABS_X} -> mnemonic {@code LDA}, suffix {@code ABS_X}) and mapping the | |
| 14 | * suffix to an {@link AddressingMode}. | |
| 15 | */ | |
| 16 | final class OpCodeTable { | |
| 17 | ||
| 18 | private static final Map<String, AddressingMode> MODE_BY_SUFFIX = Map.ofEntries( | |
| 19 | Map.entry("IMP", AddressingMode.IMPLIED), | |
| 20 | Map.entry("A", AddressingMode.ACCUMULATOR), | |
| 21 | Map.entry("I", AddressingMode.IMMEDIATE), | |
| 22 | Map.entry("Z", AddressingMode.ZERO_PAGE), | |
| 23 | Map.entry("Z_X", AddressingMode.ZERO_PAGE_X), | |
| 24 | Map.entry("Z_Y", AddressingMode.ZERO_PAGE_Y), | |
| 25 | Map.entry("ABS", AddressingMode.ABSOLUTE), | |
| 26 | Map.entry("ABS_X", AddressingMode.ABSOLUTE_X), | |
| 27 | Map.entry("ABS_Y", AddressingMode.ABSOLUTE_Y), | |
| 28 | Map.entry("IND", AddressingMode.INDIRECT), | |
| 29 | Map.entry("IND_X", AddressingMode.INDIRECT_X), | |
| 30 | Map.entry("IND_Y", AddressingMode.INDIRECT_Y), | |
| 31 | Map.entry("REL", AddressingMode.RELATIVE) | |
| 32 | ); | |
| 33 | ||
| 34 | private static final Map<String, Map<AddressingMode, MOS6502OpCode>> BY_MNEMONIC_AND_MODE = buildTable(); | |
| 35 | private static final Map<MOS6502OpCode, AddressingMode> MODE_BY_OPCODE = buildReverseTable(); | |
| 36 | ||
| 37 | private OpCodeTable() { | |
| 38 | } | |
| 39 | ||
| 40 | private static Map<String, Map<AddressingMode, MOS6502OpCode>> buildTable() { | |
| 41 | final Map<String, Map<AddressingMode, MOS6502OpCode>> table = new HashMap<>(); | |
| 42 | ||
| 43 | for (final MOS6502OpCode opcode : MOS6502OpCode.values()) { | |
| 44 | final String[] parts = opcode.name().split("_", 2); | |
| 45 | final AddressingMode mode = MODE_BY_SUFFIX.get(parts[1]); | |
| 46 | ||
| 47 | table.computeIfAbsent(parts[0], mnemonic -> new EnumMap<>(AddressingMode.class)).put(mode, opcode); | |
| 48 | } | |
| 49 | ||
| 50 | return table; | |
| 51 | } | |
| 52 | ||
| 53 | private static Map<MOS6502OpCode, AddressingMode> buildReverseTable() { | |
| 54 | final Map<MOS6502OpCode, AddressingMode> table = new EnumMap<>(MOS6502OpCode.class); | |
| 55 | ||
| 56 | for (final MOS6502OpCode opcode : MOS6502OpCode.values()) { | |
| 57 | final String[] parts = opcode.name().split("_", 2); | |
| 58 | table.put(opcode, MODE_BY_SUFFIX.get(parts[1])); | |
| 59 | } | |
| 60 | ||
| 61 | return table; | |
| 62 | } | |
| 63 | ||
| 64 | static boolean supports(final String mnemonic, final AddressingMode mode) { | |
| 65 | final Map<AddressingMode, MOS6502OpCode> modes = BY_MNEMONIC_AND_MODE.get(mnemonic); | |
| 66 |
3
1. supports : replaced boolean return with true for com/rox/cpu/mos6502/assembler/OpCodeTable::supports → KILLED 2. supports : negated conditional → KILLED 3. supports : negated conditional → KILLED |
return modes != null && modes.containsKey(mode); |
| 67 | } | |
| 68 | ||
| 69 | /** @throws IllegalStateException if the mnemonic/mode pair isn't supported - callers must check {@link #supports} first */ | |
| 70 | static MOS6502OpCode resolve(final String mnemonic, final AddressingMode mode) { | |
| 71 | final Map<AddressingMode, MOS6502OpCode> modes = BY_MNEMONIC_AND_MODE.get(mnemonic); | |
| 72 |
2
1. resolve : negated conditional → KILLED 2. resolve : negated conditional → KILLED |
if (modes == null || !modes.containsKey(mode)) { |
| 73 | throw new IllegalStateException(mnemonic + " does not support " + mode + " addressing"); | |
| 74 | } | |
| 75 |
1
1. resolve : replaced return value with null for com/rox/cpu/mos6502/assembler/OpCodeTable::resolve → KILLED |
return modes.get(mode); |
| 76 | } | |
| 77 | ||
| 78 | /** @return the addressing modes {@code mnemonic} supports, or an empty set if it's not a known mnemonic */ | |
| 79 | static Set<AddressingMode> supportedModes(final String mnemonic) { | |
| 80 | final Map<AddressingMode, MOS6502OpCode> modes = BY_MNEMONIC_AND_MODE.get(mnemonic); | |
| 81 |
2
1. supportedModes : replaced return value with Collections.emptySet for com/rox/cpu/mos6502/assembler/OpCodeTable::supportedModes → KILLED 2. supportedModes : negated conditional → KILLED |
return modes == null ? Set.of() : modes.keySet(); |
| 82 | } | |
| 83 | ||
| 84 | static AddressingMode modeOf(final MOS6502OpCode opcode) { | |
| 85 |
1
1. modeOf : replaced return value with null for com/rox/cpu/mos6502/assembler/OpCodeTable::modeOf → KILLED |
return MODE_BY_OPCODE.get(opcode); |
| 86 | } | |
| 87 | } | |
Mutations | ||
| 66 |
1.1 2.2 3.3 |
|
| 72 |
1.1 2.2 |
|
| 75 |
1.1 |
|
| 81 |
1.1 2.2 |
|
| 85 |
1.1 |