nba-emu / NanoBoyAdvance

A cycle-accurate Nintendo Game Boy Advance emulator.
GNU General Public License v3.0
955 stars 53 forks source link

Quirks in audio FIFO and DMA #326

Open alyosha-tas opened 10 months ago

alyosha-tas commented 10 months ago

I've been working on some audio FIFO tests in the style of jsmolka's suite, but quickly ran into some weird stuff. You can find the test here:

https://github.com/alyosha-tas/gba-tests/tree/master/fifo_dma

Test 1 is just testing readability of sound control reg for FIFO while power is off, nothing unexpected happens.

For test 2 though, in order for the test to pass, manual writes to FIFO while audio power is off need to fail (not add samples to FIFO.)

Additionally, FIFO DMA from fixed source in ROM needs to use all non-sequential accesses, which was unexpected, previously I had all DMA's to/from ROM automatically incrementing.

Test 3 just checks if FIFO is cleared by power off, nothing unexpected happens.

Test 4 is nearly identical to test 2 but with a few more nops in between timer reads, however the results are non-sensical. (They remain non-sensical without the nops as well.) To pass the test would require all non-sequential accesses for the first FIFO DMA and all sequential accesses for the second. I don't have an explanation for that but it works on hardware.

NanoBoyAdvance currently fails on test 2.

EDIT: Strike through comments about non-sequential accesses, they are likely incorrect. but, the cause of the observed behaviours is still unknown.

fleroviux commented 10 months ago

Thanks for this! Looks like I had an oversight regarding the APU master enable bit in 16-bit and 32-bit IO handlers.

Regarding fixed source ROM DMA: that's odd. My impression so far was that DMA forces the first ROM access to be non-sequential and otherwise uses all sequential accesses, which can look like incrementing DMA for ROM due to the cartridge latching the address only on non-sequential access. I'll try to write some tests and see if I can find out more.

fleroviux commented 10 months ago

This is really weird. I wrote some tests for DMA1-3 decrementing and fixed ROM source DMAs and they all seem to indicate that a single non-sequential + N sequential accesses are used. I haven't tested audio DMA specifically yet, admittedly, but I'm not sure why it would be different. The only explanation I can think of at the moment would be that since the DMA seems to run between the two timer reads maybe the access previously done on the bus or the access to be done by the CPU next affects the DMA? But I wasn't able to reproduce anything like that so far.

alyosha-tas commented 10 months ago

I guess another explanation could be that audio DMA simply takes longer for some unknown reason and it just happens to be 6 cycles so that it looks like non-sequential timing.. Maybe there is some internal FIFO state not being captured yet?

I'll do some more testing today and see if I can come up with anything that makes sense.

fleroviux commented 10 months ago

I have messed around with your test a bit and noticed that there appears to be a missing comparison for the tmr_read_b value around here:

        adr     r5, .tmr_read_b
        ldr     r7, [r5]
        bne     f002b
        b       t003

I have modified the code like this, which should fix that I think:

        adr     r5, .tmr_read_b
        ldr     r1, [r5]
        cmp     r7, r1
        bne     f002b
        b       t003     

But then I also had to adjust tmr_read_b to 0x0080008B to pass on hardware.

After this a couple of other observations were:

alyosha-tas commented 10 months ago

Oh, good catch, I'll commit the fixed code.

I made a slightly modified test that uses EWRAM and still got some strange results, but only if another timer overflow happens while the FIFO DMA is still running. Perhaps this is part of the issue in the original test as well. If I ensure timer ticks happen slowly enough that they don't occur concurrently with a DMA, nothing strange happens that I can see.

fleroviux commented 10 months ago

I have started to test FIFO DMA specifically a bit and noticed that my test sometimes seems to lock up on hardware when timer overflows happen during a FIFO DMA frequently enough. I wonder if the internal state of the DMA somehow gets messed up under certain circumstances causing it to never stop. I know this kind of thing can happen when stopping a DMA while it is starting up.

alyosha-tas commented 10 months ago

Here's another test that doesn't rely on strange interrupting of DMAs.

https://github.com/alyosha-tas/gba-tests/tree/master/fifo_dma_2

This one tests exact FIFO DMA timing with prefetcher enabled.