DMCChannel.java

1
package com.rox.apu;
2
3
import com.rox.clock.ClockWatcher;
4
import com.rox.mem.MemoryBus;
5
6
/**
7
 * NES delta modulation channel (DMC, $4010-$4013). Plays back 1-bit delta-encoded PCM samples
8
 * fetched via DMA from CPU-addressable memory, driving a 7-bit output/delta counter that the
9
 * {@link Mixer} reads directly as this channel's level (0-127) - unlike the other four channels,
10
 * DMC has no envelope, sweep, or length counter, and isn't clocked by the {@link FrameSequencer}
11
 * at all; its only rhythm is its own timer.
12
 *
13
 * <p>The timer ({@link ParityCountdownFrequencyDivider}, same "once per APU cycle" gating as
14
 * pulse/noise) drives an 8-bit shift register that empties one bit at a time, LSB first: a 1 bit
15
 * nudges the delta counter up by 2 (clamped at 127), a 0 bit nudges it down by 2 (clamped at 0).
16
 * Every 8 shifts, the shift register is refilled by a memory fetch, provided sample bytes remain -
17
 * if none remain, the output freezes at its last delta value (real DMC behaviour, not a
18
 * simplification) until the channel is restarted.
19
 *
20
 * <p>Sample playback parameters: start address = $C000 + ($4012 * 64), length = ($4013 * 16) + 1
21
 * bytes, fetch address wraps $FFFF back to $8000. On exhausting the sample, either the loop flag
22
 * ($4010 bit 6) restarts playback from the original address/length, or - if the loop flag is clear
23
 * and the IRQ-enable flag ($4010 bit 7) is set - an IRQ becomes pending.
24
 *
25
 * <p>Enable/disable ($4015 bit 4, via {@link #setEnabled(boolean)}) works differently from the
26
 * length-counter-based channels: disabling sets {@code bytesRemaining} to 0 directly (stops future
27
 * fetches - whatever's already latched in the shift register keeps playing out until the next
28
 * reload finds nothing left and silences); enabling only calls {@link #start()} if the channel was
29
 * idle ({@code bytesRemaining == 0}) - restarting a still-playing sample would be wrong.
30
 *
31
 * <p>Simplifications (Phase 6):
32
 * <ul>
33
 *     <li>CPU cycle-stealing/stall on each DMA sample fetch is not modeled - real hardware stalls
34
 *     the CPU 1-4 cycles per fetch; no stall mechanism exists in this codebase's Clock/ClockWatcher
35
 *     model yet. Tracked as a separate, deferred follow-up.</li>
36
 *     <li>Real hardware's memory-reader and output unit are two independent state machines linked
37
 *     only by the one-byte sample buffer (the reader can prefetch ahead of the output unit
38
 *     finishing its current byte); here they're combined into one synchronous fetch-on-refill path
39
 *     - functionally equivalent for audio output, but not a basis for cycle-accurate CPU stalling.</li>
40
 *     <li>{@link #start()} eagerly calls {@link #reloadShiftRegister()}, so starting playback of a
41
 *     1-byte sample synchronously consumes that byte - and can set {@link #isIrqPending()} or
42
 *     perform the loop reload - before a single output clock has happened. The address/length are
43
 *     only reloaded once {@code bytesRemaining} reaches 0, matching real hardware.</li>
44
 * </ul>
45
 */
46
public class DMCChannel implements ClockWatcher {
47
    /** NTSC DMC rates (in APU cycles), selected by the low nibble of $4010. */
48
    static final int[] NTSC_DMC_RATES = {
49
            428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54
50
    };
51
52
    private static final int IRQ_ENABLE_BIT = 0x80;
53
    private static final int LOOP_BIT = 0x40;
54
    private static final int RATE_INDEX_MASK = 0x0F;
55
56
    private static final int DIRECT_LOAD_MASK = 0x7F;
57
58
    private static final int SAMPLE_ADDRESS_BASE = 0xC000;
59
    private static final int SAMPLE_ADDRESS_MULTIPLIER = 64;
60
61
    private static final int SAMPLE_LENGTH_MULTIPLIER = 16;
62
    private static final int SAMPLE_LENGTH_OFFSET = 1;
63
64
    private static final int MAX_ADDRESS = 0xFFFF;
65
    private static final int ADDRESS_WRAP_TARGET = 0x8000;
66
67
    private static final int DELTA_MIN = 0;
68
    private static final int DELTA_MAX = 127;
69
    private static final int DELTA_STEP = 2;
70
71
    private static final int SHIFT_REGISTER_BITS = 8;
72
73
    private final MemoryBus memoryBus;
74
    private final ParityCountdownFrequencyDivider frequencyDivider;
75
76
    private boolean irqEnabled;
77
    private boolean loop;
78
    private boolean irqPending;
79
80
    private int sampleStartAddress;
81
    private int sampleLength;
82
83
    private int currentAddress;
84
    private int bytesRemaining;
85
86
    private int shiftRegister;
87
    private int bitsRemainingInShiftRegister;
88
    private boolean outputSilenced = true;
89
90
    private int deltaCounter;
91
92
    public DMCChannel(final MemoryBus memoryBus){
93
        this.memoryBus = memoryBus;
94
        this.frequencyDivider = new ParityCountdownFrequencyDivider(this::clockOutputUnit, false, NTSC_DMC_RATES[0]);
95
    }
96
97
    private boolean channelIsIdle(){
98 2 1. channelIsIdle : replaced boolean return with true for com/rox/apu/DMCChannel::channelIsIdle → KILLED
2. channelIsIdle : negated conditional → KILLED
        return bytesRemaining == 0;
99
    }
100
101
    /**
102
     * Handle a $4010 write.
103
     *
104
     * Register: IL-- RRRR
105
     * I (bit 7): IRQ enable
106
     * L (bit 6): loop flag
107
     * R (bits 0-3): index into {@link #NTSC_DMC_RATES}
108
     */
109
    public void writeControlRegister(final int value){
110 2 1. writeControlRegister : Replaced bitwise AND with OR → KILLED
2. writeControlRegister : negated conditional → KILLED
        irqEnabled = (value & IRQ_ENABLE_BIT) != 0;
111 2 1. writeControlRegister : Replaced bitwise AND with OR → KILLED
2. writeControlRegister : negated conditional → KILLED
        loop = (value & LOOP_BIT) != 0;
112 1 1. writeControlRegister : negated conditional → KILLED
        if (!irqEnabled){
113
            irqPending = false;
114
        }
115 2 1. writeControlRegister : Replaced bitwise AND with OR → KILLED
2. writeControlRegister : removed call to com/rox/apu/ParityCountdownFrequencyDivider::setCounterPeriod → KILLED
        frequencyDivider.setCounterPeriod(NTSC_DMC_RATES[value & RATE_INDEX_MASK]);
116
    }
117
118
    /** Register: -DDD DDDD - direct 7-bit load of the delta counter ($4011), bypassing the shift/delta logic. */
119
    public void writeDirectLoad(final int value){
120 1 1. writeDirectLoad : Replaced bitwise AND with OR → KILLED
        deltaCounter = value & DIRECT_LOAD_MASK;
121
    }
122
123
    /** Register: AAAA AAAA - sample start address = $C000 + value*64 ($4012). */
124
    public void writeSampleAddress(final int value){
125 2 1. writeSampleAddress : Replaced integer addition with subtraction → KILLED
2. writeSampleAddress : Replaced integer multiplication with division → KILLED
        sampleStartAddress = SAMPLE_ADDRESS_BASE + value * SAMPLE_ADDRESS_MULTIPLIER;
126
    }
127
128
    /** Register: LLLL LLLL - sample length in bytes = value*16 + 1 ($4013). */
129
    public void writeSampleLength(final int value){
130 2 1. writeSampleLength : Replaced integer multiplication with division → KILLED
2. writeSampleLength : Replaced integer addition with subtraction → KILLED
        sampleLength = value * SAMPLE_LENGTH_MULTIPLIER + SAMPLE_LENGTH_OFFSET;
131
    }
132
133
    /**
134
     * Starts (or restarts) sample playback: reloads the current address and bytes-remaining
135
     * counter from the last-written $4012/$4013 values, then immediately primes the shift register
136
     * (fetching the first sample byte if one is available). Called by {@link #setEnabled(boolean)}
137
     * when a $4015 write enables this channel while it was idle.
138
     */
139
    public void start(){
140
        currentAddress = sampleStartAddress;
141
        bytesRemaining = sampleLength;
142 1 1. start : removed call to com/rox/apu/DMCChannel::reloadShiftRegister → KILLED
        reloadShiftRegister();
143
    }
144
145
    /**
146
     * Handle a $4015 bit 4 enable-bit change. Enabling only restarts playback if the channel was
147
     * idle ({@code bytesRemaining == 0}) - a still-playing sample is left alone. Disabling stops
148
     * future fetches by zeroing {@code bytesRemaining}; whatever's already latched in the shift
149
     * register keeps playing out until the next reload finds nothing left and silences.
150
     */
151
    public void setEnabled(final boolean enabled){
152 1 1. setEnabled : negated conditional → KILLED
        if (!enabled){
153
            bytesRemaining = 0;
154 1 1. setEnabled : negated conditional → KILLED
        } else if (channelIsIdle()){
155 1 1. setEnabled : removed call to com/rox/apu/DMCChannel::start → KILLED
            start();
156
        }
157
    }
158
159
    /** Whether the DMA reader still has sample bytes left to fetch - for the $4015 status read. */
160
    public boolean isActive(){
161 3 1. isActive : replaced boolean return with true for com/rox/apu/DMCChannel::isActive → KILLED
2. isActive : changed conditional boundary → KILLED
3. isActive : negated conditional → KILLED
        return bytesRemaining > 0;
162
    }
163
164
    /** CPU-cycle clock: the timer runs at half this rate (once per APU cycle), same as pulse/noise. */
165
    @Override
166
    public void tick(){
167 1 1. tick : removed call to com/rox/apu/ParityCountdownFrequencyDivider::tick → KILLED
        frequencyDivider.tick();
168
    }
169
170
    private void clockOutputUnit(){
171 1 1. clockOutputUnit : negated conditional → KILLED
        if (!outputSilenced){
172 2 1. clockOutputUnit : negated conditional → KILLED
2. clockOutputUnit : Replaced bitwise AND with OR → KILLED
            if ((shiftRegister & 1) != 0){
173 1 1. clockOutputUnit : Replaced integer addition with subtraction → KILLED
                deltaCounter = Math.min(DELTA_MAX, deltaCounter + DELTA_STEP);
174
            } else {
175 1 1. clockOutputUnit : Replaced integer subtraction with addition → KILLED
                deltaCounter = Math.max(DELTA_MIN, deltaCounter - DELTA_STEP);
176
            }
177
        }
178 1 1. clockOutputUnit : Replaced Shift Right with Shift Left → KILLED
        shiftRegister >>= 1;
179 1 1. clockOutputUnit : Replaced integer subtraction with addition → KILLED
        bitsRemainingInShiftRegister--;
180 2 1. clockOutputUnit : changed conditional boundary → KILLED
2. clockOutputUnit : negated conditional → KILLED
        if (bitsRemainingInShiftRegister <= 0){
181 1 1. clockOutputUnit : removed call to com/rox/apu/DMCChannel::reloadShiftRegister → KILLED
            reloadShiftRegister();
182
        }
183
    }
184
185
    /**
186
     * Refills the 8-bit shift register for the next 8 output clocks. On real hardware this is fed
187
     * by an independent one-byte sample buffer that can be prefetched ahead of the output unit
188
     * needing it; here the fetch happens synchronously in the same call (see the class Javadoc's
189
     * simplifications), so there's no separate buffer state to model.
190
     */
191
    private void reloadShiftRegister(){
192
        bitsRemainingInShiftRegister = SHIFT_REGISTER_BITS;
193 1 1. reloadShiftRegister : negated conditional → KILLED
        if (channelIsIdle()){
194
            outputSilenced = true;
195
            return;
196
        }
197
        shiftRegister = fetchNextSampleByte();
198
        outputSilenced = false;
199
    }
200
201
    /** Fetches the next sample byte, advancing (and wrapping) the address and handling exhaustion. */
202
    private int fetchNextSampleByte(){
203
        final int value = memoryBus.read(currentAddress);
204 2 1. fetchNextSampleByte : Replaced integer addition with subtraction → KILLED
2. fetchNextSampleByte : negated conditional → KILLED
        currentAddress = currentAddress == MAX_ADDRESS ? ADDRESS_WRAP_TARGET : currentAddress + 1;
205 1 1. fetchNextSampleByte : Replaced integer subtraction with addition → KILLED
        bytesRemaining--;
206 1 1. fetchNextSampleByte : negated conditional → KILLED
        if (channelIsIdle()){
207 1 1. fetchNextSampleByte : negated conditional → KILLED
            if (loop){
208
                currentAddress = sampleStartAddress;
209
                bytesRemaining = sampleLength;
210 1 1. fetchNextSampleByte : negated conditional → KILLED
            } else if (irqEnabled){
211
                irqPending = true;
212
            }
213
        }
214 1 1. fetchNextSampleByte : replaced int return with 0 for com/rox/apu/DMCChannel::fetchNextSampleByte → KILLED
        return value;
215
    }
216
217
    /** Current output: the 7-bit delta counter (0-127), fed directly into {@link Mixer#mix}. */
218
    public int outputSample(){
219 1 1. outputSample : replaced int return with 0 for com/rox/apu/DMCChannel::outputSample → KILLED
        return deltaCounter;
220
    }
221
222
    public boolean isIrqPending(){
223 2 1. isIrqPending : replaced boolean return with false for com/rox/apu/DMCChannel::isIrqPending → KILLED
2. isIrqPending : replaced boolean return with true for com/rox/apu/DMCChannel::isIrqPending → KILLED
        return irqPending;
224
    }
225
226
    public void clearIrq(){
227
        irqPending = false;
228
    }
229
230
    int sampleStartAddress(){
231 1 1. sampleStartAddress : replaced int return with 0 for com/rox/apu/DMCChannel::sampleStartAddress → KILLED
        return sampleStartAddress;
232
    }
233
234
    int sampleLength(){
235 1 1. sampleLength : replaced int return with 0 for com/rox/apu/DMCChannel::sampleLength → KILLED
        return sampleLength;
236
    }
237
238
    int currentAddress(){
239 1 1. currentAddress : replaced int return with 0 for com/rox/apu/DMCChannel::currentAddress → KILLED
        return currentAddress;
240
    }
241
242
    int bytesRemaining(){
243 1 1. bytesRemaining : replaced int return with 0 for com/rox/apu/DMCChannel::bytesRemaining → KILLED
        return bytesRemaining;
244
    }
245
246
    int currentDeltaCounter(){
247 1 1. currentDeltaCounter : replaced int return with 0 for com/rox/apu/DMCChannel::currentDeltaCounter → KILLED
        return deltaCounter;
248
    }
249
250
    int currentShiftRegister(){
251 1 1. currentShiftRegister : replaced int return with 0 for com/rox/apu/DMCChannel::currentShiftRegister → KILLED
        return shiftRegister;
252
    }
253
254
    int bitsRemainingInShiftRegister(){
255 1 1. bitsRemainingInShiftRegister : replaced int return with 0 for com/rox/apu/DMCChannel::bitsRemainingInShiftRegister → KILLED
        return bitsRemainingInShiftRegister;
256
    }
257
258
    boolean isOutputSilenced(){
259 2 1. isOutputSilenced : replaced boolean return with true for com/rox/apu/DMCChannel::isOutputSilenced → KILLED
2. isOutputSilenced : replaced boolean return with false for com/rox/apu/DMCChannel::isOutputSilenced → KILLED
        return outputSilenced;
260
    }
261
262
    int currentTimerPeriod(){
263 1 1. currentTimerPeriod : replaced int return with 0 for com/rox/apu/DMCChannel::currentTimerPeriod → KILLED
        return frequencyDivider.getCounterPeriod();
264
    }
265
266
    boolean isLoopFlagSet(){
267 2 1. isLoopFlagSet : replaced boolean return with true for com/rox/apu/DMCChannel::isLoopFlagSet → KILLED
2. isLoopFlagSet : replaced boolean return with false for com/rox/apu/DMCChannel::isLoopFlagSet → KILLED
        return loop;
268
    }
269
270
    /** Everything a {@link DMCChannel} needs to carry on exactly where it was (sample bytes live in the cartridge, not here). */
271
    public record Snapshot(boolean irqEnabled, boolean loop, boolean irqPending, int sampleStartAddress,
272
                           int sampleLength, int currentAddress, int bytesRemaining, int shiftRegister,
273
                           int bitsRemainingInShiftRegister, boolean outputSilenced, int deltaCounter,
274
                           ParityCountdownFrequencyDivider.Snapshot frequencyDivider) {
275
    }
276
277
    public Snapshot snapshot(){
278 1 1. snapshot : replaced return value with null for com/rox/apu/DMCChannel::snapshot → KILLED
        return new Snapshot(irqEnabled, loop, irqPending, sampleStartAddress, sampleLength, currentAddress,
279
                bytesRemaining, shiftRegister, bitsRemainingInShiftRegister, outputSilenced, deltaCounter,
280
                frequencyDivider.snapshot());
281
    }
282
283
    public void restore(final Snapshot snapshot){
284
        irqEnabled = snapshot.irqEnabled();
285
        loop = snapshot.loop();
286
        irqPending = snapshot.irqPending();
287
        sampleStartAddress = snapshot.sampleStartAddress();
288
        sampleLength = snapshot.sampleLength();
289
        currentAddress = snapshot.currentAddress();
290
        bytesRemaining = snapshot.bytesRemaining();
291
        shiftRegister = snapshot.shiftRegister();
292
        bitsRemainingInShiftRegister = snapshot.bitsRemainingInShiftRegister();
293
        outputSilenced = snapshot.outputSilenced();
294
        deltaCounter = snapshot.deltaCounter();
295 1 1. restore : removed call to com/rox/apu/ParityCountdownFrequencyDivider::restore → KILLED
        frequencyDivider.restore(snapshot.frequencyDivider());
296
    }
297
}

Mutations

98

1.1
Location : channelIsIdle
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:startPrimesTheShiftRegisterByFetchingTheFirstByteWhenBytesAreAvailable()]
replaced boolean return with true for com/rox/apu/DMCChannel::channelIsIdle → KILLED

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

110

1.1
Location : writeControlRegister
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:disablingIrqViaControlRegisterClearsAPendingIrq()]
Replaced bitwise AND with OR → KILLED

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

111

1.1
Location : writeControlRegister
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:writingControlRegisterSetsLoopFlag()]
Replaced bitwise AND with OR → KILLED

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

112

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

115

1.1
Location : writeControlRegister
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[test-template:writingControlRegisterSelectsRateFromNtscTable(int)]/[test-template-invocation:#8]
Replaced bitwise AND with OR → KILLED

2.2
Location : writeControlRegister
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[test-template:writingControlRegisterSelectsRateFromNtscTable(int)]/[test-template-invocation:#16]
removed call to com/rox/apu/ParityCountdownFrequencyDivider::setCounterPeriod → KILLED

120

1.1
Location : writeDirectLoad
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:outputSampleReturnsTheDeltaCounter()]
Replaced bitwise AND with OR → KILLED

125

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

2.2
Location : writeSampleAddress
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:writingSampleAddressComputesStartAddressFromRegisterValue()]
Replaced integer multiplication with division → KILLED

130

1.1
Location : writeSampleLength
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:writingSampleLengthComputesLengthFromRegisterValue()]
Replaced integer multiplication with division → KILLED

2.2
Location : writeSampleLength
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:writingSampleLengthComputesLengthFromRegisterValue()]
Replaced integer addition with subtraction → KILLED

142

1.1
Location : start
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:isActiveReflectsWhetherBytesRemain()]
removed call to com/rox/apu/DMCChannel::reloadShiftRegister → KILLED

152

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

154

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

155

1.1
Location : setEnabled
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:enablingWhileIdleRestartsPlayback()]
removed call to com/rox/apu/DMCChannel::start → KILLED

161

1.1
Location : isActive
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:isActiveReflectsWhetherBytesRemain()]
replaced boolean return with true for com/rox/apu/DMCChannel::isActive → KILLED

2.2
Location : isActive
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:isActiveReflectsWhetherBytesRemain()]
changed conditional boundary → KILLED

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

167

1.1
Location : tick
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[test-template:tickIsParityGatedLikePulseAndNoise(int)]/[test-template-invocation:#2]
removed call to com/rox/apu/ParityCountdownFrequencyDivider::tick → KILLED

171

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

172

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

2.2
Location : clockOutputUnit
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:deltaCounterClampsAt0WhenShiftingOutAllZeroBits()]
Replaced bitwise AND with OR → KILLED

173

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

175

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

178

1.1
Location : clockOutputUnit
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:deltaCounterClampsAt127WhenShiftingOutAllOneBits()]
Replaced Shift Right with Shift Left → KILLED

179

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

180

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

2.2
Location : clockOutputUnit
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[test-template:tickIsParityGatedLikePulseAndNoise(int)]/[test-template-invocation:#2]
negated conditional → KILLED

181

1.1
Location : clockOutputUnit
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:noLoopAndIrqDisabledDoesNothingOnExhaustion()]
removed call to com/rox/apu/DMCChannel::reloadShiftRegister → KILLED

193

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

204

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

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

205

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

206

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

207

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

210

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

214

1.1
Location : fetchNextSampleByte
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:startPrimesTheShiftRegisterByFetchingTheFirstByteWhenBytesAreAvailable()]
replaced int return with 0 for com/rox/apu/DMCChannel::fetchNextSampleByte → KILLED

219

1.1
Location : outputSample
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:outputSampleReturnsTheDeltaCounter()]
replaced int return with 0 for com/rox/apu/DMCChannel::outputSample → KILLED

223

1.1
Location : isIrqPending
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:disablingIrqViaControlRegisterClearsAPendingIrq()]
replaced boolean return with false for com/rox/apu/DMCChannel::isIrqPending → KILLED

2.2
Location : isIrqPending
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:disablingIrqViaControlRegisterClearsAPendingIrq()]
replaced boolean return with true for com/rox/apu/DMCChannel::isIrqPending → KILLED

231

1.1
Location : sampleStartAddress
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:writingSampleAddressComputesStartAddressFromRegisterValue()]
replaced int return with 0 for com/rox/apu/DMCChannel::sampleStartAddress → KILLED

235

1.1
Location : sampleLength
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:writingSampleLengthComputesLengthFromRegisterValue()]
replaced int return with 0 for com/rox/apu/DMCChannel::sampleLength → KILLED

239

1.1
Location : currentAddress
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:shiftRegisterRefillsFromSuccessiveAddressesAcrossMultipleBytes()]
replaced int return with 0 for com/rox/apu/DMCChannel::currentAddress → KILLED

243

1.1
Location : bytesRemaining
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:loopFlagRestartsPlaybackFromOriginalAddressAndLengthOnExhaustion()]
replaced int return with 0 for com/rox/apu/DMCChannel::bytesRemaining → KILLED

247

1.1
Location : currentDeltaCounter
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:writingDirectLoadSetsDeltaCounterDirectlyMaskedTo7Bits()]
replaced int return with 0 for com/rox/apu/DMCChannel::currentDeltaCounter → KILLED

251

1.1
Location : currentShiftRegister
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:startPrimesTheShiftRegisterByFetchingTheFirstByteWhenBytesAreAvailable()]
replaced int return with 0 for com/rox/apu/DMCChannel::currentShiftRegister → KILLED

255

1.1
Location : bitsRemainingInShiftRegister
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[test-template:tickIsParityGatedLikePulseAndNoise(int)]/[test-template-invocation:#2]
replaced int return with 0 for com/rox/apu/DMCChannel::bitsRemainingInShiftRegister → KILLED

259

1.1
Location : isOutputSilenced
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:startPrimesTheShiftRegisterByFetchingTheFirstByteWhenBytesAreAvailable()]
replaced boolean return with true for com/rox/apu/DMCChannel::isOutputSilenced → KILLED

2.2
Location : isOutputSilenced
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:noLoopAndIrqEnabledSetsIrqPendingOnExhaustion()]
replaced boolean return with false for com/rox/apu/DMCChannel::isOutputSilenced → KILLED

263

1.1
Location : currentTimerPeriod
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[test-template:writingControlRegisterSelectsRateFromNtscTable(int)]/[test-template-invocation:#16]
replaced int return with 0 for com/rox/apu/DMCChannel::currentTimerPeriod → KILLED

267

1.1
Location : isLoopFlagSet
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:writingControlRegisterSetsLoopFlag()]
replaced boolean return with true for com/rox/apu/DMCChannel::isLoopFlagSet → KILLED

2.2
Location : isLoopFlagSet
Killed by : com.rox.apu.DMCChannelTest.[engine:junit-jupiter]/[class:com.rox.apu.DMCChannelTest]/[method:writingControlRegisterSetsLoopFlag()]
replaced boolean return with false for com/rox/apu/DMCChannel::isLoopFlagSet → KILLED

278

1.1
Location : snapshot
Killed by : com.rox.save.SaveFileFormatTest.[engine:junit-jupiter]/[class:com.rox.save.SaveFileFormatTest]/[method:readLiveSnapshotIsEmptyForASnapshotPayloadInAFileTaggedAsAnotherSaveType(java.nio.file.Path)]
replaced return value with null for com/rox/apu/DMCChannel::snapshot → KILLED

295

1.1
Location : restore
Killed by : com.rox.apu.ApuSnapshotTest.[engine:junit-jupiter]/[class:com.rox.apu.ApuSnapshotTest]/[method:restoredMidSampleRunsInLockstep()]
removed call to com/rox/apu/ParityCountdownFrequencyDivider::restore → KILLED

Active mutators

Tests examined


Report generated by PIT 1.22.1