blargg's dmg_sound-2 tests in plain English

Discussion of programming and development for the original Game Boy and Game Boy Color.
Post Reply
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

blargg's dmg_sound-2 tests in plain English

Post by Near »

Can anyone decipher blargg's cryptic test ROM comments into plain English?

I'm trying very hard to pass his tests (I was able to pass 1-6), but I can't make any sense out of the rest. Sorry if it seems disparaging, I appreciate test ROMs very much, but I really wish they could be bothered to write more than six to twelve words to explain what each one is actually testing. The source code for test ROMs is usually even -less- helpful because it's full of 80 macros that hide what's really going on.

Also, I'm not one to talk. My test ROMs are even worse. I'm just frustrated because test ROMs aren't really helpful when they're indecipherable :(

Any help would, as always, be very much appreciated.

07-len sweep period sync: #5 "Powering up APU MODs next frame time with 8192"
I have no idea what he means by frame time. But in his source, I see this for NR52 writes:

Code: Select all

		else if ( addr == status_reg && (data ^ old_data) & power_mask )
		{
			// Power control
			frame_phase = 0;
			for ( int i = osc_count; --i >= 0; )
				silence_osc( *oscs [i] );

			reset_regs();
			if ( wave.mode != mode_dmg )
				reset_lengths();

			regs [status_reg - start_addr] = data;
		}
He's resetting the frame_phase, which is the sequencer step (it goes through eight phases, which varyingly clock the lengths, sweep and envelopes.) I do the same thing as he does with frame_phase = 0, but still fail the test.

Also very bizarre is his bit-twiddling logic: "(data ^ old_data) & power_mask"
That basically causes the APU to reset itself both on 0->1 and 1->0 transitions.
I can't really believe that's how the real hardware works. Whether I do this or not, I still fail. It's just curious.

08-len ctr during power: "40 00 40 40 3cf589b4 Failed"
I'm not sure how this could be less intuitive if he tried.

09-wave read while on: <huge amount of zeroes> "7a 75e7b6 Failed"
See above. My understanding is wave RAM is always readable while on.

10-wave trigger while on: <about 30 pages of hex values that scroll off screen immediately> "Failed"
I stand corrected. Scrolling test results offscreen is even worse.

11-regs after power: #4 "Power off shouldn't affect NR41"
NR41 is the length value for square 1. I made it so that disabling the APU via NR52 doesn't affect it. The test still fails anyway. So there's more to this test than just that.

12-wave write while on: <about 30 pages of hex values that scroll off screen immediately> "Failed"
Sigh.
Last edited by Near on Thu Jan 14, 2016 1:39 pm, edited 1 time in total.
User avatar
ikari_01
Posts: 141
Joined: Sat Jul 04, 2009 2:28 pm
Location: Wunstorf, Germany

Re: blargg's dmg_sound-2 tests in plain English

Post by ikari_01 »

http://gbdev.gg8.se/wiki/articles/Gameb ... d_hardware might give you some answers, preferrably the "Obscure Behavior" section :)
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Re: blargg's dmg_sound-2 tests in plain English

Post by Near »

... wow. I thought all that site had was a wikified pandocs, that's all my Google searches pulled up.

That is exactly what I needed! Much more detailed explanations of the quirks that blargg is relying on!

Thank you very, very much! This will be immensely helpful :D
AWJ
Posts: 433
Joined: Mon Nov 10, 2008 3:09 pm

Re: blargg's dmg_sound-2 tests in plain English

Post by AWJ »

ikari_01 wrote:http://gbdev.gg8.se/wiki/articles/Gameb ... d_hardware might give you some answers, preferrably the "Obscure Behavior" section :)
That page is impossible to find unless you know the URL; no other page in the wiki links to it.
User avatar
Dwedit
Posts: 4922
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Re: blargg's dmg_sound-2 tests in plain English

Post by Dwedit »

Note that manual control of the volume envelopes does not work from GBA code. I don't know if it has been tested on GB/GBC games running on a GBA.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: blargg's dmg_sound-2 tests in plain English

Post by tepples »

AWJ wrote:That page is impossible to find unless you know the URL; no other page in the wiki links to it.
Then go create a forum account, activate it through the link sent to your e-mail, and place links to these pages where they belong.
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Re: blargg's dmg_sound-2 tests in plain English

Post by Near »

> 07-len sweep period sync: #5 "Powering up APU MODs next frame time with 8192"

blargg's code was a bit misleading, but I was able to decipher it from observing the same bit-twiddling in gambatte.

You want to clear the sequencer phase (0-7) back to zero only on a 0->1 transition of NR52.d7
If you clear it regardless (eg 0->0 or 1->1), the test fails.

> 08-len ctr during power: "40 00 40 40 3cf589b4 Failed"

I notice I'm passing for CGB mode, but not DMG mode. All two lines of comments in the source file:

Code: Select all

; On CGB, length counters are reset when powered up.
; On DMG, they are unaffected, and not clocked.
I am 100% certain the length counters are not affected on powering up. The message has nothing to do with why the tests are failing.

EDIT: ... great. Test# 8 requires writes to length registers when the APU is off to succeed, and test #1 requires those same writes to fail. It's a paradox.

> 11-regs after power: #4 "Power off shouldn't affect NR41"

Really getting annoyed with this one. I commented out 100% of the code that touches NR4x on power off/on. This message is complete nonsense and isn't the problem, but the code can't possibly be more obtuse about what it's actually testing.

EDIT 2: okay, passed all three of these. Since nobody else can explain this, I guess I'll just have to do it myself.

When the APU is disabled via NR52.d7:

1. reads from all registers work. You'll get back zero bits for the valid settings because things were reset on the NR52.d7=1->0 write. However, note that wave RAM is not flushed and still reads back as normal. Note that the NRx1 length registers aren't ever readable and always return as all 1's instead of 0's here (s you get back 3F, 3F, FF, FF [noise is 3F but has no duty, so it's FF])

2. writes most registers will fail. However, on the DMG, writes to NR11, NR21, NR31, NR41, NR52 succeed. And on the CGB, only writes to NR52 succeed. HOWEVER, DMG writes to NR11, NR21, NR31, NR41 only change the length: if NR52.d7==0, then you cannot change the duty setting that's on the same register for the two square channels.

Now all that's left are the dreaded wave read/trigger/write while on tests. Which, uh ...
Triggering the wave channel on the DMG while it reads a sample byte will alter the first four bytes of wave RAM. If the channel was reading one of the first four bytes, the only first byte will be rewritten with the byte being read. If the channel was reading one of the later 12 bytes, the first FOUR bytes of wave RAM will be rewritten with the four aligned bytes that the read was from (bytes 4-7, 8-11, or 12-15); for example if it were reading byte 9 when it was retriggered, the first four bytes would be rewritten with the contents of bytes 8-11. To avoid this corruption you should stop the wave by writing 0 then $80 to NR30 before triggering it again. The game Duck Tales encounters this issue part way through most songs.
... sounds like a bucket of fun ;_;
Near
Founder of higan project
Posts: 1553
Joined: Mon Mar 27, 2006 5:23 pm

Re: blargg's dmg_sound-2 tests in plain English

Post by Near »

09-wave read while on
If the wave channel is enabled, accessing any byte from $FF30-$FF3F is equivalent to accessing the current byte selected by the waveform position. Further, on the DMG accesses will only work in this manner if made within a couple of clocks of the wave channel accessing wave RAM; if made at any other time, reads return $FF and writes have no effect.
Is that really the best we can get in documentation? "Yeah, this thing happens, at some point, probably. What's it to you?"

Code: Select all

12557968: period=103
12558071: fetch
12558071: 00
12558073: fetch
12558075: fetch
12558077: fetch
12558079: fetch
12558081: fetch
12559850: period=102
12559952: fetch
12559953: ff
12559954: fetch
12559956: fetch
12559958: fetch
12559960: fetch
12559962: fetch
12561706: period=101
12561807: fetch
12561809: fetch
12561809: 00
12561811: fetch
12561813: fetch
12561815: fetch
12561817: fetch
12563558: period=100
12563658: fetch
12563660: fetch
12563661: ff
12563662: fetch
12563664: fetch
12563666: fetch
12563668: fetch
12565324: period=99
12565423: fetch
12565425: fetch
12565427: fetch
12565427: 11
12565429: fetch
12565431: fetch
12565433: fetch
12567086: period=98
12567184: fetch
12567186: fetch
12567188: fetch
12567189: ff
12567190: fetch
12567192: fetch
12567194: fetch
12568912: period=97
12569009: fetch
12569011: fetch
12569013: fetch
12569015: fetch
12569015: 11
12569017: fetch
12569019: fetch
12570752: period=96
12570848: fetch
12570850: fetch
12570852: fetch
12570854: fetch
12570855: ff
12570856: fetch
12570858: fetch
First off, the timing on this is obscenely brutal. You don't have to be exact on cycles, you have to be exact to a single clock tick. This kind of absolute, psychopathic perfection doesn't belong in an otherwise general test suite. Especially when the entirety of the documentation consists of a single comment in a source file:

Code: Select all

; Reads from wave RAM while playing, each time 2 clocks later.
Just to get the <pass, fail, pass, fail> instead of <fail, pass, fail, pass> required changing the CPU read handler to step 2 clock ticks, read, then step 2 clock ticks. The same kind of obscene timing that the SNES requires for latching counters.

Next up, the test is interleaved so that more and more wave RAM fetches happen before the read. You basically have to have the CPU read on the exact same cycle that the APU fetches the byte. One cycle later and on the DMG, it expects to get back FF instead. So it's not "within a couple of clocks", it's literally "instantly." One single tick of the 2MHz APU, and the value's gone and back to reading FF. Short of a sadistic test ROM like this, reading back the wave RAM would be nigh impossible while the channel was running.

When enabling the channel, you set uint5 patternOffset=0, and when you run the wave channel and the timer hits zero, then you want:

Code: Select all

    if(!(patternOffset & 1)) {
      patternByte = pattern[patternOffset >> 1];
    }
    patternOffset++;
For computing an output sample inside run:

Code: Select all

  uint4 patternSample = patternByte >> (patternOffset & 1 ? 0 : 4);
  uint4 sample = patternSample >> shift[volume];
Then for reading from wave RAM:

Code: Select all

    if(!system.cgb() && enable && (!patternAccess || !patternClocked)) result = 0xff;
    else if(enable) result = patternByte;
    else result = pattern[addr & 15];
That gets the GBC version of the test to pass, and misses only the first byte. There's almost no combination of setting the initial pattern offset to {30, 31, 0, 1}, setting the CPU clocks to {0,4; 1,3; 2,2; 3,1; 4,0}, setting the wave RAM reads to ((patternOffset-N)&31)>>1, using ++patternOffset vs patternOffset++ in wave::run, etc that can counter the fact that with this test ROM as it is, the first pattern RAM fetch from wave::run happens before the first CPU read from pattern RAM. The only workable solution is for CPU::read to be { add_clocks(1); read(); add_clocks(3); } plus making my CPU sync to APU on clock<=0 (instead of <0), and APU to CPU on clock >=0, which I don't believe for a second to be correct. Plus, {1,3} pushes the tick evens back to <pass,fail,pass,fail>, which won't pass the test.

The only thing I can do to pass the DMG test is an absolutely disgusting hack where "if the period has only expired once AND the APU is on the same clock position both when the period expired AND when the palette RAM was read by the CPU, force the value to 0xFF". But I don't know whether that 0xFF value is secretly "waveRAM[15] 0xFF" or "can't access, busy, 0xFF".

There's probably some special channel enable delays that aren't documented anywhere. Both Gb_Snd_Emu and libgambatte are full of disgusting speed hacks that play fast and loose with time shifting, making it completely impossible to reason about what's going on, timing-wise.

There's a possibility I have a DMG opcode whose cycle timings (eg read, read, i/o vs read, i/o, read) are incorrect. But I pass all the tests from gekkio around this that I know of, and it's not documented anywhere else, and there are no other test ROMs for it, so ... if that's the case, I'd be helpless to find it.
binji
Posts: 11
Joined: Thu Jun 16, 2016 11:53 am

Re: blargg's dmg_sound-2 tests in plain English

Post by binji »

I had a really hard time w/ a bunch of Blaarg's tests too (DAA was brutal), mostly because they just checksum so it's hard to know _why_ the test case is failing. For dmg_sound-2, I didn't even think to test against another emulator at first, so I was just making random changes to try to get it to work properly. :-}

Just for anyone else who is trying to do this, the expected results for test 09 are:

Code: Select all

FF FF 00 FF 11 FF 11 
FF 22 FF 22 FF 33 FF
33 FF 44 FF 44 FF 55
FF 55 FF 66 FF 66 FF
77 FF 77 FF 88 FF 88
FF 99 FF 99 FF AA FF
AA FF BB FF BB FF CC
FF CC FF DD FF DD FF
EE FF EE FF FF FF FF 
FF 00 FF 00 FF 11
Once I got the real results it was easier to figure out how to make my emulator match. Basically, the insight I had was that the CPU always ticks at 4 cycle increments, but the APU updates at 2 cycle increments. So when I started doing some printf debugging, I found that the CPU cycle counter of the read to wave ram actually matched the APU cycle counter, it's just that there was an additional APU update after that, so if I looked at the last APU read from wave ram it wouldn't match up. So I started storing the last two samples read from wave ram, along with the cycle count when it happened. The CPU checks both, and if either matches a wave ram read, it reads/writes that sample byte. That made tests 09 and 12 pass, but 10 was still failing. More printf debugging showed that the trigger tests were all off by 2 cycles. Seems like a bit of hack, but when I check for trigger by adding 2 cycles to the read, that test passes too.

I should also mention that I haven't tried running gekkio's tests (kinda wish there was just a bunch of roms so I didn't have to build them myself... lazy!), so probably none of them pass. All of Blaarg's tests (except oam_bug*) pass, though.

If you're curious, the source is here: https://github.com/binji/binjgb. I think the code is pretty straightforward and readable, one C file < 5000 lines total. :) Lots of stuff is not as fast as it could be, but most games I've tried work now.
gekkio
Posts: 49
Joined: Fri Oct 16, 2015 6:18 am

Re: blargg's dmg_sound-2 tests in plain English

Post by gekkio »

binji wrote: kinda wish there was just a bunch of roms so I didn't have to build them myself... lazy!
Here you go:

http://gekkio.fi/files/mooneye-gb/night ... wtests.zip

Despite what it says in the URL, it's actually not an automated nightly build...yet :)
binji
Posts: 11
Joined: Thu Jun 16, 2016 11:53 am

Re: blargg's dmg_sound-2 tests in plain English

Post by binji »

Awesome, thanks! :)

Actually, this already helped me fix a dumb bug in my sprite priority code. The doc I was reading made it seem like sprite priority was a sort based on X-coordinate, then OAM index... but maybe I just misunderstood. :-|
binji
Posts: 11
Joined: Thu Jun 16, 2016 11:53 am

Re: blargg's dmg_sound-2 tests in plain English

Post by binji »

I came back to this because I was curious about passing cgb_sound. It looks like it's actually much easier to get both to pass than I described above. The trick is to add a 6 cycle delay to the wave period whenever it is triggered. This doesn't seem to be mentioned anywhere in Gameboy_sound_hardware, but after I figured it out, I found the same thing in Game_Music_Emu here. :-)

I've implemented it binjgb here, if you're curious.
masterdoc
Posts: 1
Joined: Tue Feb 01, 2022 8:36 am

Re: blargg's dmg_sound-2 tests in plain English

Post by masterdoc »

Hi I saw you passed the obscure error of powering off shoudln't affect NR41, But I didn't understand you explanation, is it possible you can explain again maybe in more detail of what you did ?
Post Reply