DMC incorrect timer value

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
hypnotron
Posts: 8
Joined: Fri Apr 17, 2020 7:21 pm

DMC incorrect timer value

Post by hypnotron »

bugged-audio.mp3
(175.71 KiB) Downloaded 140 times
I'm trying to get my emulator to pass blargg's APU timer tests but am failing the DMC pitch ROM. I'm not quite sure what exactly is wrong with my DMC implementation. When run, the tones are accurate but wobble in amplitude. A recording of the bugged audio output is attached (the correct output is included with the test). Here's also the relevant DMC code (the full emulator source is here).

Code: Select all

        
template <typename CounterType>
struct Counter {
    CounterType counter;
    CounterType reload;
    std::function<void()> function;

    Counter(
            const CounterType reload,
            const std::function<void()>& function)
          : reload{reload}, function{function}, counter{reload} {
    }

    void tick(CounterType ticks = 1) {
        if((counter -= ticks) < 0) {
            counter += reload + 1;
            function();
        }
    }
}
        
        
        
        struct Dmc {
            Apu& apu;

            u8_fast irqId;
            u8_fast volume {0};
            bool irqEnabled {true};
            bool silence;
            bool enabled {false};
            bool finished;
            bool loop;
            u8_fast shiftRegister;
            //-1 indicates an empty sample buffer:
            s16_fast sampleBuffer {-1};
            u16_fast startAddress;
            u16_fast address;

            Dmc(Apu& apu)
                  : apu{apu} {
                irqId = apu.cpu.connectIrq();
            }

            s16_fast emptySampleBuffer() {
                s16_fast tmp {sampleBuffer};
                if (enabled) {
                    if (!finished) {
                        //TODO: stall cpu
                        sampleBuffer = apu.cpu.memory[address++];
                        address |= 0x8000;
                    }
                    bytesRemaining.tick();
                }
                else {
                    sampleBuffer = -1;
                }
                return tmp;
            }

            Counter<s16_fast> bytesRemaining{0, [&] () {
                if (loop) {
                    address = startAddress;
                }
                else {
                    finished = true;
                    bytesRemaining.counter = 0;
                    if (irqEnabled) {
                        apu.cpu.pullIrq(irqId);
                    }
                }
            }};

            Counter<s8_fast> bitsRemaining{7, [&] () {
                s16_fast tmp {emptySampleBuffer()};
                if (tmp == -1) {
                    silence = true;
                }
                else {
                    silence = false;
                    shiftRegister = tmp;
                }
            }};
            Counter<s16_fast> timer{0, [&] () {
                if (!silence) {
                    if (shiftRegister & 0x01 && volume <= 125) {
                        volume += 2;
                    }
                    else if (!(shiftRegister & 0x01) && volume >= 2) {
                        volume -= 2;
                    }
                }
                shiftRegister >>= 1;
                bitsRemaining.tick();
            }};

            void tick() {
                timer.tick();
                if (!irqEnabled) {
                    apu.cpu.releaseIrq(irqId);
                }
            }

            void toggle(const bool enable) {
                enabled = enable;
                bytesRemaining.counter = enabled
                      ? (finished
                            ? bytesRemaining.reload
                            : bytesRemaining.counter)
                      : 0;
                finished = !enabled;
            }

            u8_fast output() const {
                return volume;
            }
        };
User avatar
Quietust
Posts: 1920
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: DMC incorrect timer value

Post by Quietust »

That test ROM works by playing a specific pitch through both the Pulse channel and the DMC channel - the warbling sound you hear is a result of the two waves playing at almost the same frequency and interfering with each other.

Comparing your "dmcPeriods" list to the wiki shows that your values are all off by exactly 1 - increment each of them and I suspect your problem will go away.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
hypnotron
Posts: 8
Joined: Fri Apr 17, 2020 7:21 pm

Re: DMC incorrect timer value

Post by hypnotron »

audio.mp3
(175.71 KiB) Downloaded 142 times
Thanks! I was under the impression that the periods needed to be decremented by one since they reload when clocked at zero, not when they reach zero, but using the periods from the wiki fixed the major warbling. There's still another warbling/buzzing noise, however, which I suspect is related to my bytes remaining implementation (which is off by one so that I can loop/IRQ when clocked at zero rather than when it reaches zero), but I can't pinpoint the error. I've attached the new bugged audio for reference.
User avatar
Quietust
Posts: 1920
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: DMC incorrect timer value

Post by Quietust »

hypnotron wrote: Sat Apr 18, 2020 7:39 am There's still another warbling/buzzing noise, however, which I suspect is related to my bytes remaining implementation (which is off by one so that I can loop/IRQ when clocked at zero rather than when it reaches zero), but I can't pinpoint the error. I've attached the new bugged audio for reference.
That could definitely cause samples to play back incorrectly, since a $4013-write value of 1 should give you a sample length of 17 bytes rather than the 16 it looks like your code is using.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
hypnotron
Posts: 8
Joined: Fri Apr 17, 2020 7:21 pm

Re: DMC incorrect timer value

Post by hypnotron »

Interesting... my APU now completely passes blargg's apu_test but still produces that warbling sound during test_apu_timers. This may be an issue for which I'll just need to wait until a solution comes to me one day. For now, I'm more concerned with the fact the I fixed the sample buffer and passed all the blargg tests!

EDIT: The warbling was due to the one thing I hadn't properly implemented yet: the CPU DMC DMA stall. Stalling for four cycles every time, although inaccurate, eliminates the warbling at least until I take the time to write a better implementation.
Post Reply