Getting right attribute tables with scrolling

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Getting right attribute tables with scrolling
by on (#179077)
Hi ! Me again,

I've been trying to fix a bug in my emulator for 2 weeks, and I'm going crazy. You will probably laugh at my issue, since it must be very simple.

When I'm making my quadrant calculations for applying colors, it works perfectly, except on games with scrolling (only tested horizontal, but I'm sure vertical will also fail).

What I'm doing is this:

Code:
int row32 = scanline % 32;
int column32 = (i + ppuX) % 32;
if (row32 <= 15) {
   if (column32 <= 15) {
           quadrant = attrib & 0x03;         // bits 0-1
   } else {
      quadrant = (attrib & 0x0C) >> 2;      // bits 2-3
   }
} else {
   if (column32 <= 15) {
           quadrant = (attrib & 0x30) >> 4;      // bits 4-5
   } else {
           quadrant = (attrib & 0xC0) >> 6;      // bits 6-7
   }
}


SMB for example, sometimes will draw parts of the cloud with green colour, and some question marks blocks will also flicker different colours depending on how much I scroll.

I debugged, and looks like the issue is with my "column32" variable, that sometimes will pick the 4th quadrant instead of the third one which has the correct white color for the clouds.

How am I supposed to make this calculation correctly ? Something regarding the scrolling is throwing it off.

Thanks for your help
Re: Getting right attribute tables with scrolling
by on (#179080)
There's a formula on the wiki for mapping a nametable memory address to its attribute, if this is helpful:
PPU scrolling # Tile and attribute fetching
Re: Getting right attribute tables with scrolling
by on (#179081)
Yes, I'm using this one:

attribute address = 0x23C0 | (v & 0x0C00) | ((v >> 4) & 0x38) | ((v >> 2) & 0x07)

and it's working ok, the issue with the calculation of this:
http://wiki.nesdev.com/w/index.php/PPU_attribute_tables

Somehow I'm not nailing down the formula for those quadrants
Re: Getting right attribute tables with scrolling
by on (#179084)
I notice in the picture you posted, the visible errors are all from the $2400 nametable region, and the $2000 nametable looks fine. Perhaps the problem has something to do with nametable selection, and not just picking a quadrant?
Re: Getting right attribute tables with scrolling
by on (#179085)
Im not 100% sure, but, the clouds attribute is coming as 0x20, that means only the third quadrant is loaded with a palette different than 0, (palette 3).

So again, Im sure my quadrant logic is off. I dont know how to account for scrolling, Im using the fine scrollX to get the quadrant, but thats not working correctly.
Re: Getting right attribute tables with scrolling
by on (#179086)
Also maybe worth pointing out that you can deduce quadrant from the address, just like you can deduce the attribute. You don't need to be converting from a pixel location.
  • (v & 2) is the bit that decides horizontal quadrant.
  • (v & 64) is the bit that decides vertical quadrant.
Re: Getting right attribute tables with scrolling
by on (#179087)
Ok that's really helpful ! I was looking for something like that.

Is that hex numbers 0x64 or decimal 64 ?

This is what it looks like using either
Re: Getting right attribute tables with scrolling
by on (#179089)
I can't interpret that image but to explain those two values:

Every X column is 1 byte wide, so if v increments by 1, so does X. The attribute columns are 2 tiles wide, so it changes every 2 bytes. (v & 2)

Every Y row is 32 bytes wide. The attribute rows are 2 tiles tall, so it changes every two rows, 32 * 2 = 64. (v & 64)


Before you do that, though, looking at your pixel-based attempt, are you maybe just adding the scroll value when you should be subtracting it?
Re: Getting right attribute tables with scrolling
by on (#179090)
With my previous attempt , yes I tried adding and substracting, and it looked different, but still wrong.

I implemented your approach as this:

Code:
                if ((ppuV & 64) != 64) {
         // 1 or 2
         if ((ppuV & 2) == 2) {     QUADRANT 1
            quadrant = attrib & 0x03;
         } else {           QUADRANT 2
            quadrant = (attrib & 0x0C) >> 2;
         }
      } else {
         // 3 or 4
         if ((ppuV & 2) == 2) {
            quadrant = (attrib & 0x30) >> 4;      // QUADRANT 3
         } else {
            quadrant = (attrib & 0xC0) >> 6;      // QUADRANT 4
         }


So I'm only using the V register, not the X one. Now it looks like this:
Re: Getting right attribute tables with scrolling
by on (#179092)
You're doing something on a per-pixel basis instead of per-tile. V only changes on a tile, it should be impossible to see an attribute split halfway through a tile like in your screenshot. Are you somehow using a different V for your attributes than you are to fetch the tile, changing it on every pixel?
Re: Getting right attribute tables with scrolling
by on (#179096)
mmm, looks like I'm not changing V on a pixel basis.

However, I found something suspicious with the tile fetching.

So, according to that wiki, I'm storing first and second tiles on PPU cycles 328 and 336 of previous scanline (same for attributes), and then tile 3 at 8 cycles of current scanline, tile 4 at 16 cycles, etc.

This is how I'm getting the current tile for drawing:
tile = arrayOfTiles[(currentPixel + ppuX) / 8];

That is, if I'm rendering the third pixel of the scanline, and ppuX = 7, then (2 + 7) / 8 = 1, fetch the secondTile, and do the same with the attribute.

If I remove ppuX from there, I get correct attributes, but scrolling is super trippy.

I'm close, but something is still missing.
Re: Getting right attribute tables with scrolling
by on (#179460)
Im going crazy.

No matter what combination I use, when I get correct colours, the scrolling is glitched, and when scrolling works, I get those incorrect colors in some tiles.

Any tips on what should I check ?

I'm caching the first and second tiles on video cycles 328 and 336 of the previous line, and the rest on cycles 8, 16, etc of the current line.

How does X register account for tile fetching ? .. Im using (currentDot + X) / 8 = Index of my array of prefetched tiles,

Thanks,
Re: Getting right attribute tables with scrolling
by on (#179514)
DarkMoe wrote:
How does X register account for tile fetching ? .. Im using (currentDot + X) / 8 = Index of my array of prefetched tiles

The upper 5 bits of the X scroll register affect tile fetching (combined with Y scroll and the 2 nametable select bits), and they're incremented (just the upper 5 bits) after each tile.

The bottom 3 bits of the X scroll register, on the other hand, never change - the only effect they have is on which tile pixels get drawn to the screen, effectively shifting the entire scanline left by up to 7 pixels.
Re: Getting right attribute tables with scrolling
by on (#179521)
DarkMoe wrote:
How does X register account for tile fetching ? .. Im using (currentDot + X) / 8 = Index of my array of prefetched tiles

You should be fetching the tiles using the PPU address, which should increment (after also fetching the attribute), then at the end of each scanline reload the PPU address and increase it by 1 line.

There should be no way that attributes and tiles get out of synch with each other; they should both be fetched based on the same PPU address, and cached together however you're doing that. Don't do one thing for tiles and a different thing for attributes.

(There's more precise information about the timing on the wiki but they're less important than solving the primary problem you're having of the tiles not being synchronized with their attributes.)
Re: Getting right attribute tables with scrolling
by on (#179992)
Narrowed it down to quadrant fetching.

When there's fine X scroll involved, quadrant number and attributes data don't match.

So, I guess I need to add that fine x scroll field to quadrant logic ?

My current logic:

Code:
                if ((ppuV & 64) != 64) {
         if (((ppuV + ppuX) & 2) == 2) {
            quadrantPalette = (attrib & 0x03);         // quadrant 1
         } else {
            quadrantPalette = (attrib & 0x0C) >> 2;      // quadrant 2
         }
      } else {
         if (((ppuV + ppuX) & 2) == 2) {
            quadrantPalette = (attrib & 0x30) >> 4;      // quadrant 3
                        } else {
            quadrantPalette = (attrib & 0xC0) >> 6;      // quadrant 4
         }
      }


Still can't get it right, looks like I'm getting quadrant 4 for those white clouds, when I should be getting quadrant 3. Only happens when fine x scroll > 0 (at x scroll = 7, the entire tile is wrongly colored)

Any idea ? Thanks,
Re: Getting right attribute tables with scrolling
by on (#179995)
The fine X scroll should have no influence on the attributes at all! The AT address can easily be derived from the NT address:

Code:
NN: name table;
JIHGF: coarse Y scroll;
EDCBA: coarse X scroll;
NT address: %0010NNJI HGFEDCBA
AT address: %0010NN11 11JIHEDC
Quadrant: %GB

So, if you fetch a tile from %00100101 01111010, simply get its attributes from the byte at %00100111 11010110, and use quadrant %10, while completely ignoring the lowest bit of the coarse X and Y scrolls (attributes are applied to 16x16-pixel areas after all) and the fine scroll. The address of a tile in the name tables is directly linked to the address of the attribute byte it uses.
Re: Getting right attribute tables with scrolling
by on (#180265)
Followed all your advise, and got closer to the right behaviour.

So what was happening, I was trying to fetch tiles using the fine x scroll value, in order to draw pixels directly, grabbing attributes the same way, but when I calculated the quadrant using the V register, I would always be off by 0-7, depending on the fine X scroll value (except when there was no scroll).

So, what I did now, is to ignore the fine x scroll, and then move shift all pixels to the left using the fine X scroll value. Using my current logic, that leaves now the right pixels undrawn, since I'm calling the draw pixel function 256 times (and the first times don't draw anything if there's scroll).

Is there a way, to include the fine x scroll into the calculation of the quadrant ?? That would solve everything I think.
Re: Getting right attribute tables with scrolling
by on (#180268)
DarkMoe wrote:
So, what I did now, is to ignore the fine x scroll, and then move shift all pixels to the left using the fine X scroll value.

It turns out that's exactly what the PPU actually does. Fine X scroll has absolutely nothing to do with how tiles are fetched. The PPU has a set of shift registers used as a variable delay line to move the background 8-1 pixels to the right for fine X scroll values of 0-7.
Re: Getting right attribute tables with scrolling
by on (#180277)
Excellent, thank you !
Re: Getting right attribute tables with scrolling
by on (#180290)
One more question though.

If the PPU render a pixel for each PPU cycle, and cycle 0 is idle, it means that each pixel is rendered between cycles 1-256 on each scanline right ?

How does that work when there's a fine X scroll ? if I just discard the 7 first pixels (if fine X is 7), then I'm missing the right 7 pixels (the last pixels of the scanline).

I think I should bring another tile when fine scroll X is not 0, but how does it fit with the PPU timings ?
Re: Getting right attribute tables with scrolling
by on (#180291)
On each scanline, your PPU should fetch 34 tile columns during the following pixels:

  • Fetch 1 is at X=321 to 328.
  • Fetch 2 is at X=329 to 336.
  • Fetch 3 has a false start at X=337, waits a few microseconds, and restarts at X=1 to 8. The MMC5 depends on this false start to detect a new scanline.
  • Fetch 4 is at X=9 to 16.
  • ...
  • Fetch 33 (used for the partial row of tiles at right) is at X=241 to 248. The PPU uses this data for the partial column at far right whenever fine X scroll is nonzero. Otherwise, the PPU discards this fetched data.
  • Fetch 34 (discarded) is at X=249 to 256. The PPU always discards this data, but games using the MMC2 and MMC4 depend on this discarded fetch to reset the bank number to the starting point.
Re: Getting right attribute tables with scrolling
by on (#180292)
You couldn't make it any clear.

You're awesome, thank you !