Page 1 of 1

Slight timing(?) issue

Posted: Mon May 17, 2021 2:35 am
by lemmurg
This scanline test is showing some corruption in my emu:
timing1.gif
timing1.gif (5.96 KiB) Viewed 4659 times
I think it's a problem with timing, but I have a hard time finding the cause. Maybe you experienced guys have some pointers?

I suspect/hope it's also the cause for this glitch in SMB that only occurs when a sprite is near the top of the screen. It renders perfectly otherwise.
timing2.gif
timing2.gif (207.9 KiB) Viewed 4659 times

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 5:54 am
by Dwedit
Are you checking for collision with sprites that aren't Sprite #0?

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 6:57 am
by Zepper
I have the same problem with scanline test, but no shaking.

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 7:47 am
by lemmurg
Dwedit wrote: Mon May 17, 2021 5:54 am Are you checking for collision with sprites that aren't Sprite #0?
I have triple checked, I'm only testing sprite #0.

It passes these tests, except for the timing test #9. That one fails with "Flag set too soon for upper-left corner". If I add a one-cycle delay, it fails with "Flag set too late for upper-right corner" instead :?

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 8:17 am
by Quietust
lemmurg wrote: Mon May 17, 2021 7:47 am
Dwedit wrote: Mon May 17, 2021 5:54 am Are you checking for collision with sprites that aren't Sprite #0?
I have triple checked, I'm only testing sprite #0.
Are you absolutely certain? There's a big difference between "the first sprite rendered on the scanline" and "the sprite located at OAM address 0", and I strongly suspect your emulator is doing the former rather than the latter.

In fact, I'm almost 99% certain that that's what you're doing, because when I edited my own emulator to drop the "scanline contains sprite #0" check from its sprite 0 hit logic, it began behaving exactly the same as depicted in your first post.

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 8:40 am
by lemmurg
Quietust wrote: Mon May 17, 2021 8:17 am Are you absolutely certain? There's a big difference between "the first sprite rendered on the scanline" and "the sprite located at OAM address 0", and I strongly suspect your emulator is doing the former rather than the latter.
Yes, I am certain. I'm iterating over a copy of the secondary OAM starting at index 0 and the flag can only get set during the first iteration. There is no "sort by x coordinate" anywhere in the code.

Code: Select all

// just the basic logic, not the literal code
int hitFlag = 0x40 * min(1, bgPattern);
for(int i = 0; i < numSpr; ++i) {
    // range check, etc...
   if(sprPattern[i] > 0)
    status |= hitFlag;
  hitFlag = 0;
}

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 8:50 am
by Dwedit
If you look at the first sprite from secondary OAM, you'll find the first sprite rendered on the line, which is not necessarily Sprite #0. It needs to actually be sprite #0.

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 9:06 am
by lemmurg
Dwedit wrote: Mon May 17, 2021 8:50 am If you look at the first sprite from secondary OAM, you'll find the first sprite rendered on the line, which is not necessarily Sprite #0. It needs to actually be sprite #0.
So Sprite #0 is the one at index 0 in the primary OAM, not the one at index 0 in the secondary OAM? Really? Where does the PPU get this information from?

The first sprite rendered on the current line is the first one in secondary OAM that is in range of the current pixel's x position - that's not necessarily the first one in the secondary OAM. The sprite at OAM2[0] can be at x = 200 and the one at OAM2[4] can be at x = 10. They're not sorted by x coordinate. I'm only checking the one at OAM2[0], but I gotta check again if I misunderstood something about this.

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 9:11 am
by Dwedit
It would take a total of one bit of storage to indicate that Sprite 0 in secondary OAM is the same as Sprite 0 in primary OAM. You don't need anything fancy like expanding secondary OAM to include things like 'original sprite number' or 'is this sprite 0' on each sprite.

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 9:20 am
by lemmurg
Dwedit wrote: Mon May 17, 2021 9:11 am It would take a total of one bit of storage to indicate that Sprite 0 in secondary OAM is the same as Sprite 0 in primary OAM. You don't need anything fancy like expanding secondary OAM to include things like 'original sprite number' or 'is this sprite 0' on each sprite.
Sure, but I haven't seen anything about this flag in the docs, that's why I'm asking. Maybe I missed it.

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 9:26 am
by Dwedit
Using a flag is just one possible way to implement it, so that flag doesn't really formally exist anywhere, so it wouldn't be in any documentation.
The only part that matters is if it's Primary OAM Sprite #0 or not.

I don't know if the actual PPU uses a single bit of storage to define this relation, but the actual decapped chip is available to look at. It's very hard to read it.

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 9:35 am
by Quietust
Dwedit wrote: Mon May 17, 2021 9:26 am I don't know if the actual PPU uses a single bit of storage to define this relation, but the actual decapped chip is available to look at. It's very hard to read it.
The PPU actually has two internal registers for this: one to denote that sprite #0 was detected during evaluation for the next scanline ("[/]sprite_0_on_next_scanline", nodes #5934/#5907), and another one to denote that sprite #0 is being rendered on the current scanline ("[/]sprite_0_on_cur_scanline", nodes #5834/#1105), where the first gets copied to the second during cycles 256-319 (i.e. during the sprite CHR fetches). The signal names and node numbers refer to the Visual 2C02 simulator.
lemmurg wrote: Mon May 17, 2021 9:06 am So Sprite #0 is the one at index 0 in the primary OAM, not the one at index 0 in the secondary OAM? Really? Where does the PPU get this information from?
The actual logic is surprisingly simple - if the PPU tries to write to secondary OAM on cycle 66* (i.e. immediately after evaluating the Y-coordinate of sprite 0), then it sets the flag because it just evaluated Primary OAM slot #0 as a valid sprite; if the first matched sprite was primary OAM slot #1, then the write would've happened at cycle 68, and other sprites would be found at correspondingly later times.

(* the relevant signal in Visual 2C02 is actually named "++hpos_eq_65_and_rendering_and_pclk1", but it's delayed by several clock cycles so it doesn't trigger until hpos=66)

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 9:47 am
by lemmurg
I am now a proud user of this flag and the glitch in SMB went away. Thank you!

However, the scanline test doesn't care about sprite 0 hit at all, or sprites in general. It's still flickering.

Re: Slight timing(?) issue

Posted: Mon May 17, 2021 10:03 am
by Quietust
lemmurg wrote: Mon May 17, 2021 9:47 am I am now a proud user of this flag and the glitch in SMB went away. Thank you!

However, the scanline test doesn't care about sprite 0 hit at all, or sprites in general. It's still flickering.
It's been a while since I've tested scanline.nes on real hardware, but the flickering you're seeing might be a result of performing your VRAM address "vertical increment" at the wrong time.

What that ROM does is write $04->$2006/$04->$2006 just to the right of the vertical bar and then write to $2006+$2005+$2005+$2006 (to prepare the VRAM address for the next scanline) at the very right edge of the screen, so if the timing is even slightly off, it could result in the jittering you're seeing.

Re: Slight timing(?) issue

Posted: Tue May 18, 2021 12:05 pm
by lemmurg
scanline2.png
NMI timing was off on the CPU side. Some garbage in the first area, but that's also there in NEStopia and Nintendulator, so I guess I'm good?

I'm struggling with IRQ timing in general. Bunch of test are failing, all related to interrupts. I'm not really sure how long the delay between request and execution should be. Some sources say execute as soon as the current instruction ends, others (and failing tests) say after the next instruction, plus a bunch of special cases whose implementation is gonna be pretty hacky :|