Sprite zero hit issues

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Sprite zero hit issues
by on (#163437)
Hi everybody. I've written an emulator in the past in C that worked fairly well. Right now I'm doing another one in VB6. Yeah, it's a terrible language. Especially for something like an emulator. It sparked from some joking around in an IRC channel, but then I realized I was bored enough to give it a try.

So, I'm having some weird issues with sprite zero hits. Take a look at it running SMB: https://www.youtube.com/watch?v=s31k6l-kZ_0

(YouTube cuts the FPS down to 25 or 30, well that's annoying)

The emulator is working nicely for most games that don't rely on the zero hit: https://www.youtube.com/watch?v=YZiaUAAoLbY

Any ideas here? I can post some code if needed. Hate to resort to bugging the forum, but I've been banging my head against this for like 3 days. No amount of reading the wiki has helped. It's such a simple concept, and I don't recall having any issues when I wrote an emu in C. I've even compared code between them and fail to find my error.
Re: Sprite zero hit issues
by on (#163445)
99.99999% of emu problems can be debugged through examining tracelogs. If your emu has no way to dump tracelogs, that should be your top priority at this point.

Let's see a tracelog with key times highlighted:

- $2005 and $2006 writes, as well as the 'time' (scanline & cyc) the write occurs, and how those writes are changing loopy_t and loopy_v
- Scroll (loopy_t and loopy_v) at start of each scanline during rendering
- What time sprite 0 is actually hit
- What time $2002 is read with sprite 0 flag set


Given that we know (or at least it's easy to find out) when sprite 0 should trip, and when SMB should be changing the scroll, and what it should be changing it too -- we can contrast that with what your emu is doing to see clear as day what your emu is doing wrong. Then you'll know where to look to fix it.
Re: Sprite zero hit issues
by on (#163515)
Good idea Disch. By the way, this is only a scanline-accurate emulator. Not going for down-to-the-pixel precision, at least not right now.

Here's some info from frame 32 (the first one that sprite zero hits on):

Code:
==================
EXEC FRAME 32
Scanline 0: loopy Y = 0, loopy H = 0
Scanline 1: loopy Y = 0, loopy H = 0
Scanline 2: loopy Y = 0, loopy H = 0
Scanline 3: loopy Y = 0, loopy H = 0
Scanline 4: loopy Y = 0, loopy H = 0
Scanline 5: loopy Y = 0, loopy H = 0
Scanline 6: loopy Y = 0, loopy H = 0
Scanline 7: loopy Y = 0, loopy H = 0
Scanline 8: loopy Y = 1, loopy H = 0
Scanline 9: loopy Y = 1, loopy H = 0
Scanline 10: loopy Y = 1, loopy H = 0
Scanline 11: loopy Y = 1, loopy H = 0
Scanline 12: loopy Y = 1, loopy H = 0
Scanline 13: loopy Y = 1, loopy H = 0
Scanline 14: loopy Y = 1, loopy H = 0
Scanline 15: loopy Y = 1, loopy H = 0
Scanline 16: loopy Y = 2, loopy H = 0
Scanline 17: loopy Y = 2, loopy H = 0
Scanline 18: loopy Y = 2, loopy H = 0
Scanline 19: loopy Y = 2, loopy H = 0
Scanline 20: loopy Y = 2, loopy H = 0
Scanline 21: loopy Y = 2, loopy H = 0
Scanline 22: loopy Y = 2, loopy H = 0
Scanline 23: loopy Y = 2, loopy H = 0
Scanline 24: loopy Y = 3, loopy H = 0
Scanline 25: loopy Y = 3, loopy H = 0
Scanline 26: loopy Y = 3, loopy H = 0
Scanline 27: loopy Y = 3, loopy H = 0
Scanline 28: loopy Y = 3, loopy H = 0
Scanline 29: loopy Y = 3, loopy H = 0
    sprite 0 hit X = 89
    Read $2002 with sprite 0 hit set
Scanline 30: loopy Y = 3, loopy H = 0
    Write $2005 = $0
    Write $2005 = $0
Scanline 31: loopy Y = 3, loopy H = 0
Scanline 32: loopy Y = 3, loopy H = 0
Scanline 33: loopy Y = 3, loopy H = 0
Scanline 34: loopy Y = 3, loopy H = 0
Scanline 35: loopy Y = 3, loopy H = 0
Scanline 36: loopy Y = 3, loopy H = 0
Scanline 37: loopy Y = 3, loopy H = 0
Scanline 38: loopy Y = 3, loopy H = 0
Scanline 39: loopy Y = 4, loopy H = 0
Scanline 40: loopy Y = 4, loopy H = 0
Scanline 41: loopy Y = 4, loopy H = 0
Scanline 42: loopy Y = 4, loopy H = 0
Scanline 43: loopy Y = 4, loopy H = 0
Scanline 44: loopy Y = 4, loopy H = 0
Scanline 45: loopy Y = 4, loopy H = 0
Scanline 46: loopy Y = 4, loopy H = 0
Scanline 47: loopy Y = 5, loopy H = 0
Scanline 48: loopy Y = 5, loopy H = 0
Scanline 49: loopy Y = 5, loopy H = 0
Scanline 50: loopy Y = 5, loopy H = 0
Scanline 51: loopy Y = 5, loopy H = 0
Scanline 52: loopy Y = 5, loopy H = 0
Scanline 53: loopy Y = 5, loopy H = 0
Scanline 54: loopy Y = 5, loopy H = 0
Scanline 55: loopy Y = 6, loopy H = 0
Scanline 56: loopy Y = 6, loopy H = 0
Scanline 57: loopy Y = 6, loopy H = 0
Scanline 58: loopy Y = 6, loopy H = 0
Scanline 59: loopy Y = 6, loopy H = 0
Scanline 60: loopy Y = 6, loopy H = 0
Scanline 61: loopy Y = 6, loopy H = 0
Scanline 62: loopy Y = 6, loopy H = 0
Scanline 63: loopy Y = 7, loopy H = 0
Scanline 64: loopy Y = 7, loopy H = 0
    Read $2002 with sprite 0 hit set
Scanline 65: loopy Y = 7, loopy H = 0
Scanline 66: loopy Y = 7, loopy H = 0
Scanline 67: loopy Y = 7, loopy H = 0
Scanline 68: loopy Y = 7, loopy H = 0
Scanline 69: loopy Y = 7, loopy H = 0
Scanline 70: loopy Y = 7, loopy H = 0
Scanline 71: loopy Y = 8, loopy H = 0
Scanline 72: loopy Y = 8, loopy H = 0
Scanline 73: loopy Y = 8, loopy H = 0
Scanline 74: loopy Y = 8, loopy H = 0
Scanline 75: loopy Y = 8, loopy H = 0
Scanline 76: loopy Y = 8, loopy H = 0
Scanline 77: loopy Y = 8, loopy H = 0
Scanline 78: loopy Y = 8, loopy H = 0
Scanline 79: loopy Y = 9, loopy H = 0
Scanline 80: loopy Y = 9, loopy H = 0
Scanline 81: loopy Y = 9, loopy H = 0
Scanline 82: loopy Y = 9, loopy H = 0
Scanline 83: loopy Y = 9, loopy H = 0
Scanline 84: loopy Y = 9, loopy H = 0
Scanline 85: loopy Y = 9, loopy H = 0
Scanline 86: loopy Y = 9, loopy H = 0
Scanline 87: loopy Y = 10, loopy H = 0
Scanline 88: loopy Y = 10, loopy H = 0
Scanline 89: loopy Y = 10, loopy H = 0
Scanline 90: loopy Y = 10, loopy H = 0
Scanline 91: loopy Y = 10, loopy H = 0
Scanline 92: loopy Y = 10, loopy H = 0
Scanline 93: loopy Y = 10, loopy H = 0
Scanline 94: loopy Y = 10, loopy H = 0
Scanline 95: loopy Y = 11, loopy H = 0
Scanline 96: loopy Y = 11, loopy H = 0
Scanline 97: loopy Y = 11, loopy H = 0
Scanline 98: loopy Y = 11, loopy H = 0
Scanline 99: loopy Y = 11, loopy H = 0
Scanline 100: loopy Y = 11, loopy H = 0
Scanline 101: loopy Y = 11, loopy H = 0
Scanline 102: loopy Y = 11, loopy H = 0
Scanline 103: loopy Y = 12, loopy H = 0
Scanline 104: loopy Y = 12, loopy H = 0
Scanline 105: loopy Y = 12, loopy H = 0
Scanline 106: loopy Y = 12, loopy H = 0
Scanline 107: loopy Y = 12, loopy H = 0
Scanline 108: loopy Y = 12, loopy H = 0
Scanline 109: loopy Y = 12, loopy H = 0
Scanline 110: loopy Y = 12, loopy H = 0
Scanline 111: loopy Y = 13, loopy H = 0
Scanline 112: loopy Y = 13, loopy H = 0
Scanline 113: loopy Y = 13, loopy H = 0
Scanline 114: loopy Y = 13, loopy H = 0
Scanline 115: loopy Y = 13, loopy H = 0
Scanline 116: loopy Y = 13, loopy H = 0
Scanline 117: loopy Y = 13, loopy H = 0
Scanline 118: loopy Y = 13, loopy H = 0
Scanline 119: loopy Y = 14, loopy H = 0
Scanline 120: loopy Y = 14, loopy H = 0
Scanline 121: loopy Y = 14, loopy H = 0
Scanline 122: loopy Y = 14, loopy H = 0
Scanline 123: loopy Y = 14, loopy H = 0
Scanline 124: loopy Y = 14, loopy H = 0
Scanline 125: loopy Y = 14, loopy H = 0
Scanline 126: loopy Y = 14, loopy H = 0
Scanline 127: loopy Y = 15, loopy H = 0
Scanline 128: loopy Y = 15, loopy H = 0
Scanline 129: loopy Y = 15, loopy H = 0
Scanline 130: loopy Y = 15, loopy H = 0
Scanline 131: loopy Y = 15, loopy H = 0
Scanline 132: loopy Y = 15, loopy H = 0
Scanline 133: loopy Y = 15, loopy H = 0
Scanline 134: loopy Y = 15, loopy H = 0
Scanline 135: loopy Y = 16, loopy H = 0
Scanline 136: loopy Y = 16, loopy H = 0
Scanline 137: loopy Y = 16, loopy H = 0
Scanline 138: loopy Y = 16, loopy H = 0
Scanline 139: loopy Y = 16, loopy H = 0
Scanline 140: loopy Y = 16, loopy H = 0
Scanline 141: loopy Y = 16, loopy H = 0
Scanline 142: loopy Y = 16, loopy H = 0
Scanline 143: loopy Y = 17, loopy H = 0
Scanline 144: loopy Y = 17, loopy H = 0
Scanline 145: loopy Y = 17, loopy H = 0
Scanline 146: loopy Y = 17, loopy H = 0
Scanline 147: loopy Y = 17, loopy H = 0
Scanline 148: loopy Y = 17, loopy H = 0
Scanline 149: loopy Y = 17, loopy H = 0
Scanline 150: loopy Y = 17, loopy H = 0
Scanline 151: loopy Y = 18, loopy H = 0
Scanline 152: loopy Y = 18, loopy H = 0
Scanline 153: loopy Y = 18, loopy H = 0
Scanline 154: loopy Y = 18, loopy H = 0
Scanline 155: loopy Y = 18, loopy H = 0
Scanline 156: loopy Y = 18, loopy H = 0
Scanline 157: loopy Y = 18, loopy H = 0
Scanline 158: loopy Y = 18, loopy H = 0
Scanline 159: loopy Y = 19, loopy H = 0
Scanline 160: loopy Y = 19, loopy H = 0
Scanline 161: loopy Y = 19, loopy H = 0
Scanline 162: loopy Y = 19, loopy H = 0
Scanline 163: loopy Y = 19, loopy H = 0
Scanline 164: loopy Y = 19, loopy H = 0
Scanline 165: loopy Y = 19, loopy H = 0
Scanline 166: loopy Y = 19, loopy H = 0
Scanline 167: loopy Y = 20, loopy H = 0
Scanline 168: loopy Y = 20, loopy H = 0
Scanline 169: loopy Y = 20, loopy H = 0
Scanline 170: loopy Y = 20, loopy H = 0
Scanline 171: loopy Y = 20, loopy H = 0
Scanline 172: loopy Y = 20, loopy H = 0
Scanline 173: loopy Y = 20, loopy H = 0
Scanline 174: loopy Y = 20, loopy H = 0
Scanline 175: loopy Y = 21, loopy H = 0
Scanline 176: loopy Y = 21, loopy H = 0
Scanline 177: loopy Y = 21, loopy H = 0
Scanline 178: loopy Y = 21, loopy H = 0
Scanline 179: loopy Y = 21, loopy H = 0
Scanline 180: loopy Y = 21, loopy H = 0
Scanline 181: loopy Y = 21, loopy H = 0
Scanline 182: loopy Y = 21, loopy H = 0
Scanline 183: loopy Y = 22, loopy H = 0
Scanline 184: loopy Y = 22, loopy H = 0
Scanline 185: loopy Y = 22, loopy H = 0
Scanline 186: loopy Y = 22, loopy H = 0
Scanline 187: loopy Y = 22, loopy H = 0
Scanline 188: loopy Y = 22, loopy H = 0
Scanline 189: loopy Y = 22, loopy H = 0
Scanline 190: loopy Y = 22, loopy H = 0
Scanline 191: loopy Y = 23, loopy H = 0
Scanline 192: loopy Y = 23, loopy H = 0
Scanline 193: loopy Y = 23, loopy H = 0
Scanline 194: loopy Y = 23, loopy H = 0
Scanline 195: loopy Y = 23, loopy H = 0
Scanline 196: loopy Y = 23, loopy H = 0
Scanline 197: loopy Y = 23, loopy H = 0
Scanline 198: loopy Y = 23, loopy H = 0
Scanline 199: loopy Y = 24, loopy H = 0
Scanline 200: loopy Y = 24, loopy H = 0
Scanline 201: loopy Y = 24, loopy H = 0
Scanline 202: loopy Y = 24, loopy H = 0
Scanline 203: loopy Y = 24, loopy H = 0
Scanline 204: loopy Y = 24, loopy H = 0
Scanline 205: loopy Y = 24, loopy H = 0
Scanline 206: loopy Y = 24, loopy H = 0
Scanline 207: loopy Y = 25, loopy H = 0
Scanline 208: loopy Y = 25, loopy H = 0
Scanline 209: loopy Y = 25, loopy H = 0
Scanline 210: loopy Y = 25, loopy H = 0
Scanline 211: loopy Y = 25, loopy H = 0
Scanline 212: loopy Y = 25, loopy H = 0
Scanline 213: loopy Y = 25, loopy H = 0
Scanline 214: loopy Y = 25, loopy H = 0
Scanline 215: loopy Y = 26, loopy H = 0
Scanline 216: loopy Y = 26, loopy H = 0
Scanline 217: loopy Y = 26, loopy H = 0
Scanline 218: loopy Y = 26, loopy H = 0
Scanline 219: loopy Y = 26, loopy H = 0
Scanline 220: loopy Y = 26, loopy H = 0
Scanline 221: loopy Y = 26, loopy H = 0
Scanline 222: loopy Y = 26, loopy H = 0
Scanline 223: loopy Y = 27, loopy H = 0
Scanline 224: loopy Y = 27, loopy H = 0
Scanline 225: loopy Y = 27, loopy H = 0
Scanline 226: loopy Y = 27, loopy H = 0
Scanline 227: loopy Y = 27, loopy H = 0
Scanline 228: loopy Y = 27, loopy H = 0
Scanline 229: loopy Y = 27, loopy H = 0
Scanline 230: loopy Y = 27, loopy H = 0
Scanline 231: loopy Y = 28, loopy H = 0
Scanline 232: loopy Y = 28, loopy H = 0
Scanline 233: loopy Y = 28, loopy H = 0
Scanline 234: loopy Y = 28, loopy H = 0
Scanline 235: loopy Y = 28, loopy H = 0
Scanline 236: loopy Y = 28, loopy H = 0
Scanline 237: loopy Y = 28, loopy H = 0
Scanline 238: loopy Y = 28, loopy H = 0
Scanline 239: loopy Y = 29, loopy H = 0
    Read $2002 with sprite 0 hit set
    Write $2005 = $0
    Write $2005 = $0
    Read $2002 with sprite 0 hit set
    Write $2006 = $3F
    Write $2006 = $10
    Write $2006 = $3F
    Write $2006 = $0
    Write $2006 = $0
    Write $2006 = $0
    Read $2002 with sprite 0 hit set
    Write $2005 = $0
    Write $2005 = $0
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set
    Read $2002 with sprite 0 hit set

Re: Sprite zero hit issues
by on (#163521)
Looks like you're only showing Coarse Y (tile number). Fine Y (pixel number) would give more detail, but this should be good enough!

Take a look here:

Code:
Scanline 15: loopy Y = 1, loopy H = 0
Scanline 16: loopy Y = 2, loopy H = 0
...
Scanline 23: loopy Y = 2, loopy H = 0
Scanline 24: loopy Y = 3, loopy H = 0


Coarse Y=2 for exactly 8 scanlines, which is what I would expect.

Now take a look at Y=3 (where the split occurs):

Code:
Scanline 23: loopy Y = 2, loopy H = 0
Scanline 24: loopy Y = 3, loopy H = 0
... split occurs in here ...
Scanline 38: loopy Y = 3, loopy H = 0
Scanline 39: loopy Y = 4, loopy H = 0


Y=3 for 15 scanlines!!!

Note that the game is only writing to $2005, and not to $2006... and with that mechanism alone it is impossible to change the Y scroll.

So the problem must be in the way you're handling $2005 writes. Can you post that code here? It looks like you are resetting fine Y scroll or something.
Re: Sprite zero hit issues
by on (#163522)
Good eye. Here's what I've got for $2005 writes:

Code:
        Case &H2005
            If TRACEPPU = 1 Then
                Print #200, "    Write $2005 = $" + Hex$(value)
            End If
            If (PPU.addrlatch = 0) Then
                PPU.xscroll = value And 7
                PPU.tempaddr = (PPU.tempaddr And 65504) Or (value \ 8)
                PPU.addrlatch = 1
            Else
                PPU.yscroll = value And 7
                PPU.tempaddr = (PPU.tempaddr And 64543) Or ((value And 248) * 4&)
                PPU.addrlatch = 0
            End If


The thing is though, that it matches the code from my C emulator which plays SMB flawlessly. (Several large chunks were taken from it and directly ported to VB6 syntax)

Code:
      case 0x2005:
         if (PPU->addrlatch == 0) {
            PPU->xscroll = value & 7;
            PPU->tempaddr = (PPU->tempaddr & 0xFFE0) | ((uint16_t)value >> 3);
            PPU->addrlatch = 1;
         } else {
            PPU->yscroll = value & 7;
            PPU->tempaddr = (PPU->tempaddr & 0xFC1F) | (((uint16_t)value & 0xF8) << 2);
            PPU->addrlatch = 0;
         }
         break;
Re: Sprite zero hit issues
by on (#163525)
Code:
PPU.yscroll = value And 7


That's what's killing you.

Note that $2005 NEVER changes the PPU address directly. It only changes the Temp address and the fine X scroll. But FineY, CoarseY, and CourseX are all represented as bitfields in the PPU Addr -- so $2005 never changes them.

The Temp address gets copied to the PPU address at key points in the frame (when rendering enabled):
- PPU = Temp (fully copy) shortly before the end of the prerender scanline.
- PPU.X = Temp.X (X only copy) every scanline during 'HBlank'


SMB is writing to $2005 with the expectation of it changing Temp -- and only the relevant X scroll bits being copied from Temp to PPU Addr during rendering.


Quote:
The thing is though, that it matches the code from my C emulator which plays SMB flawlessly.


That doesn't really matter. You shouldn't worry about what your previous emu did and should only concern yourself with what the actual system does.

And in this case, the actual system does not touch the Y scroll.
Re: Sprite zero hit issues
by on (#163527)
Disch wrote:
Code:
PPU.yscroll = value And 7


That's what's killing you.

Note that $2005 NEVER changes the PPU address directly. It only changes the Temp address and the fine X scroll. But FineY, CoarseY, and CourseX are all represented as bitfields in the PPU Addr -- so $2005 never changes them.

The Temp address gets copied to the PPU address at key points in the frame (when rendering enabled):
- PPU = Temp (fully copy) shortly before the end of the prerender scanline.
- PPU.X = Temp.X (X only copy) every scanline during 'HBlank'


SMB is writing to $2005 with the expectation of it changing Temp -- and only the relevant X scroll bits being copied from Temp to PPU Addr during rendering.


That's it!! Thanks! This was driving me insane. :P

All looks perfect now:

Image




Disch wrote:
Quote:
The thing is though, that it matches the code from my C emulator which plays SMB flawlessly.


That doesn't really matter. You shouldn't worry about what your previous emu did and should only concern yourself with what the actual system does.

And in this case, the actual system does not touch the Y scroll.


Which brings me to the question of why the hell it is working right in the C emu anyway! I'll take a look into that later I guess. For now, it's time to make more mappers work in VB.
Re: Sprite zero hit issues
by on (#163532)
Glad it's working! :D
Re: Sprite zero hit issues
by on (#163554)
So... it wasn't a sprite zero hit problem after all. ;)
Re: Sprite zero hit issues
by on (#163583)
Yep I shouldn't assume things like that, emulators are complex beasts!