| 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 |
|
| 39 |
1.1 |
|
| 45 |
1.1 2.2 |
|
| 55 |
1.1 2.2 3.3 |
|
| 56 |
1.1 |
|
| 61 |
1.1 2.2 |
|
| 65 |
1.1 |
|
| 73 |
1.1 |