LengthCounter.java

1
package com.rox.apu;
2
3
import com.rox.clock.ClockWatcher;
4
5
/**
6
 * NES length counter, shared by the pulse, triangle and noise channels. Silences its channel once
7
 * it decays to 0 unless halted, clocked once per half-frame.
8
 *
9
 * The halt flag is the same physical bit as the owning channel's envelope loop flag (pulse/noise)
10
 * or linear-counter control flag (triangle) - a hardware quirk, not a coincidence.
11
 *
12
 * Also holds the channel's $4015 enable bit, which on real hardware only ever acts on this counter:
13
 * <ul>
14
 * <li>disabled - the counter is held at 0, so the channel is silent</li>
15
 * <li>a load while disabled is ignored</li>
16
 * </ul>
17
 */
18
public class LengthCounter implements ClockWatcher {
19
    static final int[] LENGTH_TABLE = {
20
            10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14,
21
            12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30
22
    };
23
24
    private int counter;
25
    private boolean haltEnabled;
26
    /** The channel's $4015 enable bit - off at power-on. */
27
    private boolean enabled;
28
29
    /** Loads the counter from the table - ignored while disabled. */
30
    public void load(final int index){
31 1 1. load : negated conditional → KILLED
        if (enabled){
32
            counter = LENGTH_TABLE[index];
33
        }
34
    }
35
36
    /** A $4015 enable-bit change: disabling immediately forces the counter to 0. */
37
    public void setEnabled(final boolean enabled){
38
        this.enabled = enabled;
39 1 1. setEnabled : negated conditional → KILLED
        if (!enabled){
40
            counter = 0;
41
        }
42
    }
43
44
    boolean isEnabled(){
45 2 1. isEnabled : replaced boolean return with false for com/rox/apu/LengthCounter::isEnabled → KILLED
2. isEnabled : replaced boolean return with true for com/rox/apu/LengthCounter::isEnabled → KILLED
        return enabled;
46
    }
47
48
    public void setHalt(final boolean halt){
49
        haltEnabled = halt;
50
    }
51
52
    /** Half-frame clock: decrements the counter unless halted or already at 0. */
53
    @Override
54
    public void tick(){
55 3 1. tick : changed conditional boundary → KILLED
2. tick : negated conditional → KILLED
3. tick : negated conditional → KILLED
        if (!haltEnabled && counter > 0){
56 1 1. tick : Replaced integer subtraction with addition → KILLED
            counter--;
57
        }
58
    }
59
60
    public boolean isZero(){
61 2 1. isZero : replaced boolean return with true for com/rox/apu/LengthCounter::isZero → KILLED
2. isZero : negated conditional → KILLED
        return counter == 0;
62
    }
63
64
    public int value(){
65 1 1. value : replaced int return with 0 for com/rox/apu/LengthCounter::value → KILLED
        return counter;
66
    }
67
68
    /** Everything a {@link LengthCounter} needs to carry on exactly where it was. */
69
    public record Snapshot(int counter, boolean haltEnabled, boolean enabled) {
70
    }
71
72
    public Snapshot snapshot(){
73 1 1. snapshot : replaced return value with null for com/rox/apu/LengthCounter::snapshot → KILLED
        return new Snapshot(counter, haltEnabled, enabled);
74
    }
75
76
    public void restore(final Snapshot snapshot){
77
        counter = snapshot.counter();
78
        haltEnabled = snapshot.haltEnabled();
79
        enabled = snapshot.enabled();
80
    }
81
}

Mutations

31

1.1
Location : load
Killed by : com.rox.apu.LengthCounterTest.[engine:junit-jupiter]/[class:com.rox.apu.LengthCounterTest]/[test-template:loadSetsCounterFromTable(int, int)]/[test-template-invocation:#20]
negated conditional → KILLED

39

1.1
Location : setEnabled
Killed by : com.rox.apu.LengthCounterTest.[engine:junit-jupiter]/[class:com.rox.apu.LengthCounterTest]/[method:disablingOverridesHalt()]
negated conditional → KILLED

45

1.1
Location : isEnabled
Killed by : com.rox.apu.LengthCounterTest.[engine:junit-jupiter]/[class:com.rox.apu.LengthCounterTest]/[method:enablingDoesNotChangeTheCounter()]
replaced boolean return with false for com/rox/apu/LengthCounter::isEnabled → KILLED

2.2
Location : isEnabled
Killed by : com.rox.apu.LengthCounterTest.[engine:junit-jupiter]/[class:com.rox.apu.LengthCounterTest]/[method:disablingImmediatelyZeroesALoadedCounter()]
replaced boolean return with true for com/rox/apu/LengthCounter::isEnabled → KILLED

55

1.1
Location : tick
Killed by : com.rox.apu.LengthCounterTest.[engine:junit-jupiter]/[class:com.rox.apu.LengthCounterTest]/[method:clockHalfFrameStopsAtZero()]
changed conditional boundary → KILLED

2.2
Location : tick
Killed by : com.rox.apu.LengthCounterTest.[engine:junit-jupiter]/[class:com.rox.apu.LengthCounterTest]/[method:haltFreezesCounterValue()]
negated conditional → KILLED

3.3
Location : tick
Killed by : com.rox.apu.LengthCounterTest.[engine:junit-jupiter]/[class:com.rox.apu.LengthCounterTest]/[method:clearingHaltResumesDecrementing()]
negated conditional → KILLED

56

1.1
Location : tick
Killed by : com.rox.apu.LengthCounterTest.[engine:junit-jupiter]/[class:com.rox.apu.LengthCounterTest]/[method:clockHalfFrameDecrementsLoadedCounter()]
Replaced integer subtraction with addition → KILLED

61

1.1
Location : isZero
Killed by : com.rox.apu.LengthCounterTest.[engine:junit-jupiter]/[class:com.rox.apu.LengthCounterTest]/[method:loadedCounterIsNotZero()]
replaced boolean return with true for com/rox/apu/LengthCounter::isZero → KILLED

2.2
Location : isZero
Killed by : com.rox.apu.LengthCounterTest.[engine:junit-jupiter]/[class:com.rox.apu.LengthCounterTest]/[method:loadedCounterIsNotZero()]
negated conditional → KILLED

65

1.1
Location : value
Killed by : com.rox.apu.LengthCounterTest.[engine:junit-jupiter]/[class:com.rox.apu.LengthCounterTest]/[test-template:loadSetsCounterFromTable(int, int)]/[test-template-invocation:#20]
replaced int return with 0 for com/rox/apu/LengthCounter::value → KILLED

73

1.1
Location : snapshot
Killed by : com.rox.apu.ApuSnapshotTest.[engine:jqwik]/[class:com.rox.apu.ApuSnapshotTest]/[property:lengthCounterRoundTrips(int, boolean, boolean)]
replaced return value with null for com/rox/apu/LengthCounter::snapshot → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1