Skip to content

Commit d06190b

Browse files
committed
Tests and fixes for sweep
1 parent d5cc063 commit d06190b

File tree

2 files changed

+125
-4
lines changed

2 files changed

+125
-4
lines changed

jeebie/audio/apu.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ func (a *APU) tickSweep() {
185185
if !ch.sweepEnabled {
186186
return
187187
}
188-
if ch.sweepPeriod == 0 || ch.sweepStep == 0 {
188+
if ch.sweepStep == 0 {
189189
return
190190
}
191191

@@ -433,6 +433,9 @@ func (a *APU) mapRegistersToState() {
433433
// On trigger, reset sweep timer and shadow frequency
434434
a.ch[0].sweepEnabled = a.ch[0].sweepPeriod > 0 || a.ch[0].sweepStep > 0
435435
a.ch[0].sweepTimer = a.ch[0].sweepPeriod
436+
if a.ch[0].sweepTimer == 0 {
437+
a.ch[0].sweepTimer = 8
438+
}
436439
a.ch[0].shadowFreq = a.ch[0].period
437440

438441
// Dummy calculation to immediately disable channel if overflow

jeebie/audio/apu_test.go

Lines changed: 121 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,125 @@ func TestAPU_SampleGeneration(t *testing.T) {
153153
t.Skip("Sample generation not implemented yet")
154154
}
155155

156-
// TODO: Add tests for trigger behavior
157-
func TestAPU_TriggerBehavior(t *testing.T) {
158-
t.Skip("Trigger behavior not implemented yet")
156+
func TestAPU_SweepCalculation(t *testing.T) {
157+
tests := []struct {
158+
name string
159+
shadowFreq uint16
160+
sweepStep uint8
161+
sweepDown bool
162+
wantFreq uint16
163+
wantOverflow bool
164+
}{
165+
{
166+
name: "no shift returns same frequency",
167+
shadowFreq: 1024, sweepStep: 0, sweepDown: false,
168+
wantFreq: 1024, wantOverflow: false,
169+
},
170+
{
171+
name: "sweep up increases period",
172+
shadowFreq: 1024, sweepStep: 1, sweepDown: false,
173+
wantFreq: 1536, wantOverflow: false, // 1024 + (1024 >> 1)
174+
},
175+
{
176+
name: "sweep down decreases period",
177+
shadowFreq: 1024, sweepStep: 2, sweepDown: true,
178+
wantFreq: 768, wantOverflow: false, // 1024 - (1024 >> 2)
179+
},
180+
{
181+
name: "overflow detection",
182+
shadowFreq: 2000, sweepStep: 3, sweepDown: false,
183+
wantFreq: 2250, wantOverflow: true, // 2000 + (2000 >> 3) = 2250 > 2047
184+
},
185+
}
186+
187+
for _, tt := range tests {
188+
t.Run(tt.name, func(t *testing.T) {
189+
ch := &Channel{
190+
shadowFreq: tt.shadowFreq,
191+
sweepStep: tt.sweepStep,
192+
sweepDown: tt.sweepDown,
193+
}
194+
gotFreq, gotOverflow := ch.calculateSweepFrequency()
195+
assert.Equal(t, tt.wantFreq, gotFreq)
196+
assert.Equal(t, tt.wantOverflow, gotOverflow)
197+
})
198+
}
199+
}
200+
201+
func TestAPU_SweepTrigger(t *testing.T) {
202+
apu := New()
203+
apu.WriteRegister(addr.NR52, 0x80) // Enable APU
204+
apu.WriteRegister(addr.NR10, 0x11) // period=1, up, shift=1
205+
apu.WriteRegister(addr.NR13, 0x00)
206+
apu.WriteRegister(addr.NR14, 0x04) // Frequency = 0x400
207+
apu.WriteRegister(addr.NR12, 0xF0) // Enable DAC
208+
apu.WriteRegister(addr.NR14, 0x84) // Trigger
209+
210+
assert.True(t, apu.ch[0].sweepEnabled)
211+
assert.Equal(t, uint16(1024), apu.ch[0].shadowFreq)
212+
assert.Equal(t, uint8(1), apu.ch[0].sweepTimer)
213+
assert.True(t, apu.ch[0].enabled)
214+
}
215+
216+
func TestAPU_SweepOverflow(t *testing.T) {
217+
apu := New()
218+
apu.WriteRegister(addr.NR52, 0x80)
219+
apu.WriteRegister(addr.NR10, 0x01) // period=0, up, shift=1
220+
apu.WriteRegister(addr.NR13, 0xFF)
221+
apu.WriteRegister(addr.NR14, 0x07) // Frequency = 0x7FF (2047)
222+
apu.WriteRegister(addr.NR12, 0xF0)
223+
apu.WriteRegister(addr.NR14, 0x87) // Trigger
224+
225+
assert.False(t, apu.ch[0].enabled, "Channel should be disabled due to overflow")
226+
}
227+
228+
func TestAPU_SweepTimer(t *testing.T) {
229+
apu := New()
230+
apu.WriteRegister(addr.NR52, 0x80)
231+
apu.WriteRegister(addr.NR10, 0x21) // period=2, up, shift=1
232+
apu.WriteRegister(addr.NR13, 0x00)
233+
apu.WriteRegister(addr.NR14, 0x04) // Frequency = 0x400
234+
apu.WriteRegister(addr.NR12, 0xF0)
235+
apu.WriteRegister(addr.NR14, 0x84) // Trigger
236+
237+
initialFreq := apu.ch[0].shadowFreq
238+
239+
apu.tickSweep()
240+
assert.Equal(t, initialFreq, apu.ch[0].shadowFreq, "No change on first tick")
241+
242+
apu.tickSweep()
243+
expectedFreq := uint16(1536) // 1024 + 512
244+
assert.Equal(t, expectedFreq, apu.ch[0].shadowFreq)
245+
assert.Equal(t, expectedFreq, apu.ch[0].period)
246+
247+
reconstructed := uint16(apu.NR14&0x07)<<8 | uint16(apu.NR13)
248+
assert.Equal(t, expectedFreq, reconstructed, "NR13/NR14 should be updated")
249+
}
250+
251+
func TestAPU_LengthTimer(t *testing.T) {
252+
apu := New()
253+
apu.WriteRegister(addr.NR52, 0x80)
254+
apu.WriteRegister(addr.NR12, 0xF0)
255+
apu.WriteRegister(addr.NR11, 0x3F) // Length timer = 63
256+
apu.WriteRegister(addr.NR14, 0xC0) // Trigger with length enable
257+
258+
assert.Equal(t, uint16(1), apu.ch[0].length) // 64 - 63 = 1
259+
assert.True(t, apu.ch[0].enabled)
260+
261+
apu.tickLength()
262+
assert.False(t, apu.ch[0].enabled, "Channel should be disabled after length expires")
263+
}
264+
265+
func TestAPU_CH3LengthTimer(t *testing.T) {
266+
apu := New()
267+
apu.WriteRegister(addr.NR52, 0x80)
268+
apu.WriteRegister(addr.NR30, 0x80) // Enable CH3 DAC
269+
apu.WriteRegister(addr.NR31, 0xFF) // Length timer = 255
270+
apu.WriteRegister(addr.NR34, 0xC0) // Trigger with length enable
271+
272+
assert.Equal(t, uint16(1), apu.ch[2].length) // 256 - 255 = 1
273+
assert.True(t, apu.ch[2].enabled)
274+
275+
apu.tickLength()
276+
assert.False(t, apu.ch[2].enabled, "CH3 should be disabled after length expires")
159277
}

0 commit comments

Comments
 (0)