ControllerPort.java

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
Location : strobe
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:reStrobingWithoutGoingHighFirstDoesNotReLatch()]
negated conditional → KILLED

2.2
Location : strobe
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:readsPastTheEighthAlwaysReturnOneInStandardMode()]
negated conditional → KILLED

58

1.1
Location : strobe
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:reStrobingWithoutGoingHighFirstDoesNotReLatch()]
negated conditional → KILLED

59

1.1
Location : strobe
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:readsPastTheEighthAlwaysReturnOneInStandardMode()]
removed call to com/rox/input/ControllerPort::latch → KILLED

64

1.1
Location : read
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:redundantStrobeHighCallsDoNotTriggerASpuriousLatch()]
negated conditional → KILLED

65

1.1
Location : read
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:redundantStrobeHighCallsDoNotTriggerASpuriousLatch()]
negated conditional → KILLED

67

1.1
Location : read
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:readsPastTheEighthAlwaysReturnOneInStandardMode()]
changed conditional boundary → KILLED

2.2
Location : read
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:aButtonChangeAfterStrobeLowDoesNotPerturbTheAlreadyLatchedSnapshot()]
negated conditional → KILLED

68

1.1
Location : read
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:readsPastTheEighthAlwaysReturnOneInStandardMode()]
replaced int return with 0 for com/rox/input/ControllerPort::read → KILLED

70

1.1
Location : read
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:readsPastTheEighthAlwaysReturnOneInStandardMode()]
replaced int return with 0 for com/rox/input/ControllerPort::read → KILLED

2.2
Location : read
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:readsPastTheEighthAlwaysReturnOneInStandardMode()]
Replaced Unsigned Shift Right with Shift Left → KILLED

3.3
Location : read
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:aButtonChangeAfterStrobeLowDoesNotPerturbTheAlreadyLatchedSnapshot()]
Replaced bitwise AND with OR → KILLED

4.4
Location : read
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:readsPastTheEighthAlwaysReturnOneInStandardMode()]
Replaced integer addition with subtraction → KILLED

75

1.1
Location : latch
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:readsPastTheEighthAlwaysReturnOneInStandardMode()]
negated conditional → KILLED

76

1.1
Location : latch
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:fourScorePort1LatchesSecondaryControllerAndItsOwnSignatureBit()]
Replaced Shift Left with Shift Right → KILLED

2.2
Location : latch
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:fourScorePort1LatchesSecondaryControllerAndItsOwnSignatureBit()]
Replaced bitwise OR with AND → KILLED

77

1.1
Location : latch
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:fourScorePort1LatchesSecondaryControllerAndItsOwnSignatureBit()]
Replaced bitwise OR with AND → KILLED

79

1.1
Location : latch
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:readsPastTheEighthAlwaysReturnOneInStandardMode()]
Replaced bitwise OR with AND → KILLED

88

1.1
Location : buttonBits
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:aButtonChangeAfterStrobeLowDoesNotPerturbTheAlreadyLatchedSnapshot()]
negated conditional → KILLED

89

1.1
Location : buttonBits
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:eightReadsDrainInAbSelectStartUpDownLeftRightOrder()]
Replaced bitwise OR with AND → KILLED

2.2
Location : buttonBits
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:eightReadsDrainInAbSelectStartUpDownLeftRightOrder()]
Replaced Shift Left with Shift Right → KILLED

92

1.1
Location : buttonBits
Killed by : com.rox.input.ControllerPortTest.[engine:junit-jupiter]/[class:com.rox.input.ControllerPortTest]/[method:eightReadsDrainInAbSelectStartUpDownLeftRightOrder()]
replaced int return with 0 for com/rox/input/ControllerPort::buttonBits → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1