| 1 | package com.rox.input; | |
| 2 | ||
| 3 | /** | |
| 4 | * One physical controller port (what $4016 or $4017 reads/writes drive) - bit-accurate emulation of | |
| 5 | * the real shift-register/strobe protocol, including the "Four Score" adapter's extended protocol for | |
| 6 | * a 3rd/4th player sharing the same port. | |
| 7 | * | |
| 8 | * Strobe high ($4016 bit0 = 1): reads continuously reflect the primary controller's live A-button | |
| 9 | * state (no latch, no advance) - real hardware behaves the same way while held high. Strobe going low | |
| 10 | * (1→0) latches a snapshot of every button into a fixed 24-bit layout and resets the read cursor to | |
| 11 | * bit 0: | |
| 12 | * <ul> | |
| 13 | * <li>bits 0-7: primary controller, one bit per {@link Button} (ordinal = bit position)</li> | |
| 14 | * <li>bits 8-15: secondary controller if Four Score is enabled, else forced to 1</li> | |
| 15 | * <li>one caller-supplied bit (see {@link #FOUR_SCORE_PORT_1_SIGNATURE_BIT}/ | |
| 16 | * {@link #FOUR_SCORE_PORT_2_SIGNATURE_BIT}) forced to 1 when Four Score is enabled - 0 | |
| 17 | * otherwise; every other bit in 16-23 stays 0 when Four Score is enabled, or forced to 1 | |
| 18 | * otherwise</li> | |
| 19 | * </ul> | |
| 20 | * Each read after the latch returns the next bit in that layout; once past bit 23 (covering both the | |
| 21 | * standard "8 reads then 1 forever" case and the Four Score tail), every further read returns 1 | |
| 22 | * without touching the latch again - matching real hardware's open-bus-style behaviour and avoiding | |
| 23 | * an unbounded read counter. | |
| 24 | */ | |
| 25 | public class ControllerPort { | |
| 26 | /** Four Score signature bit for the port carrying controllers 1 and 3 ($4016) - verified against nesdev.org's Four Score page, NOT the same bit $4017 uses. */ | |
| 27 | public static final int FOUR_SCORE_PORT_1_SIGNATURE_BIT = 1 << 19; | |
| 28 | /** Four Score signature bit for the port carrying controllers 2 and 4 ($4017) - verified against nesdev.org's Four Score page, NOT the same bit $4016 uses. */ | |
| 29 | public static final int FOUR_SCORE_PORT_2_SIGNATURE_BIT = 1 << 18; | |
| 30 | ||
| 31 | private static final int LATCHED_BIT_COUNT = 24; | |
| 32 | //bits 8-23 forced to 1 outside Four Score mode - a non-Four-Score pad has nothing there, and real | |
| 33 | //hardware's open bus reads as 1 past the 8 real buttons | |
| 34 | private static final int STANDARD_TAIL_MASK = 0xFFFF00; | |
| 35 | private static final int SECONDARY_CONTROLLER_SHIFT = 8; | |
| 36 | ||
| 37 | private final Controller primary; | |
| 38 | private final Controller secondary; | |
| 39 | private final boolean fourScoreEnabled; | |
| 40 | private final int fourScoreSignatureBit; | |
| 41 | ||
| 42 | private boolean strobeHigh; | |
| 43 | private int latchedBits; | |
| 44 | private int nextReadBit; | |
| 45 | ||
| 46 | public ControllerPort(final Controller primary, final Controller secondary, final boolean fourScoreEnabled, | |
| 47 | final int fourScoreSignatureBit){ | |
| 48 | this.primary = primary; | |
| 49 | this.secondary = secondary; | |
| 50 | this.fourScoreEnabled = fourScoreEnabled; | |
| 51 | this.fourScoreSignatureBit = fourScoreSignatureBit; | |
| 52 | } | |
| 53 | ||
| 54 | /** @param high the new state of $4016 bit 0 - a 1→0 transition latches a fresh snapshot. */ | |
| 55 | public void strobe(final boolean high){ | |
| 56 |
2
1. strobe : negated conditional → KILLED 2. strobe : negated conditional → KILLED |
final boolean fallingEdge = strobeHigh && !high; |
| 57 | strobeHigh = high; | |
| 58 |
1
1. strobe : negated conditional → KILLED |
if (fallingEdge){ |
| 59 |
1
1. strobe : removed call to com/rox/input/ControllerPort::latch → KILLED |
latch(); |
| 60 | } | |
| 61 | } | |
| 62 | ||
| 63 | public int read(){ | |
| 64 |
1
1. read : negated conditional → KILLED |
if (strobeHigh){ |
| 65 |
1
1. read : negated conditional → KILLED |
return primary.isPressed(Button.A) ? 1 : 0; |
| 66 | } | |
| 67 |
2
1. read : changed conditional boundary → KILLED 2. read : negated conditional → KILLED |
if (nextReadBit >= LATCHED_BIT_COUNT){ |
| 68 |
1
1. read : replaced int return with 0 for com/rox/input/ControllerPort::read → KILLED |
return 1; |
| 69 | } | |
| 70 |
4
1. read : replaced int return with 0 for com/rox/input/ControllerPort::read → KILLED 2. read : Replaced Unsigned Shift Right with Shift Left → KILLED 3. read : Replaced bitwise AND with OR → KILLED 4. read : Replaced integer addition with subtraction → KILLED |
return (latchedBits >>> nextReadBit++) & 1; |
| 71 | } | |
| 72 | ||
| 73 | private void latch(){ | |
| 74 | int bits = buttonBits(primary); | |
| 75 |
1
1. latch : negated conditional → KILLED |
if (fourScoreEnabled){ |
| 76 |
2
1. latch : Replaced Shift Left with Shift Right → KILLED 2. latch : Replaced bitwise OR with AND → KILLED |
bits |= buttonBits(secondary) << SECONDARY_CONTROLLER_SHIFT; |
| 77 |
1
1. latch : Replaced bitwise OR with AND → KILLED |
bits |= fourScoreSignatureBit; |
| 78 | } else { | |
| 79 |
1
1. latch : Replaced bitwise OR with AND → KILLED |
bits |= STANDARD_TAIL_MASK; |
| 80 | } | |
| 81 | latchedBits = bits; | |
| 82 | nextReadBit = 0; | |
| 83 | } | |
| 84 | ||
| 85 | private static int buttonBits(final Controller controller){ | |
| 86 | int bits = 0; | |
| 87 | for (final Button button : Button.values()){ | |
| 88 |
1
1. buttonBits : negated conditional → KILLED |
if (controller.isPressed(button)){ |
| 89 |
2
1. buttonBits : Replaced bitwise OR with AND → KILLED 2. buttonBits : Replaced Shift Left with Shift Right → KILLED |
bits |= 1 << button.ordinal(); |
| 90 | } | |
| 91 | } | |
| 92 |
1
1. buttonBits : replaced int return with 0 for com/rox/input/ControllerPort::buttonBits → KILLED |
return bits; |
| 93 | } | |
| 94 | } | |
Mutations | ||
| 56 |
1.1 2.2 |
|
| 58 |
1.1 |
|
| 59 |
1.1 |
|
| 64 |
1.1 |
|
| 65 |
1.1 |
|
| 67 |
1.1 2.2 |
|
| 68 |
1.1 |
|
| 70 |
1.1 2.2 3.3 4.4 |
|
| 75 |
1.1 |
|
| 76 |
1.1 2.2 |
|
| 77 |
1.1 |
|
| 79 |
1.1 |
|
| 88 |
1.1 |
|
| 89 |
1.1 2.2 |
|
| 92 |
1.1 |