This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Attribute updates

Attribute updates
by on (#13180)
Hello, everyone.

I have created my sidescroller level engine, and when I was done creating it, I realized that I forgot all about attributes! Then I thought, "How the hell am I going to do this?"

My game uses 2x2 metatiles, but it uses game objects. Like my routine says, "Store object at $xxxx into RAM", and an arrangement of metatiles is stored into RAM, then put on the screen when necissary. I was thinking this could be easy, but what if 2 objects share the same attribute "block"? It will be alot more complicated, and I think I'd have to keep the attribute bytes in RAM, so I could do some sort of and/ora thing, then store it on screen. What do you think?

by on (#13187)
Many games do keep a copy of the attribute table for a column, row, or entire screen depending on how they scroll.

by on (#13191)
I'm in the middle of a huge struggle with attributes myself... I guess most of the time it feels like a waste to store a copy of the attributes in RAM.

My advice goes something like this: You probably update a column of metatiles to the screen every 16 pixels the camera moves, right? So, you do something similar for the attributes, but every 32 pixels. Every 32 pixels, you activate a routine that will read your decoded screens in RAM and create an appropriate attribute byte for every square of 4 metatiles from the top to the bottom of the screen. Save that to a buffer, to output during VBlank. This way you won't need any buffers.

That's how I'd do if I were only scrolling sideways. But I'm also scrolling vertically and the part where the name tables meet makes it all difficult.

by on (#13193)
Are you doing a 2-screen-tall level (like Super Mario Bros. 3), or are you doing true 4-way scrolling?

by on (#13196)
Quote:
So, you do something similar for the attributes, but every 32 pixels.


Aren't attributes applied in 16-pixel chunks? If so, that implies that you'd want to update attributes every 16 pixels, not 32.

by on (#13197)
No, attributes are applied per 32 pixels. You could update it per 16 pixels, but that'd be just way too complicated. Or more complicated than 32 pixels. 1 byte is stored for a 4x4 tile chunk. 2 bits for each 2x2 section.

by on (#13198)
Celius wrote:
No, attributes are applied per 32 pixels.


But you can control each 16x16 square, not just every 32x32 pixel square. If you only update every 32 pixels, you will have LARGE color distortion on the edge of the screen as it scrolls (at best, 16 pixels on either end)

Quote:
You could update it per 16 pixels, but that'd be just way too complicated.


Not really. Many commercial games keep $80 bytes of RAM reserved for attribute tables (kind of like how it keeps $100 bytes for sprite data, each $40 bytes of this reserved data is an exact copy of what's in the attribute tables). As the screen scrolls you can update the RAM copy of attribute at will -- then in VBlank, you can copy the whole-shabang to the real attribute table with ease.


Games often have a whole byte to indicate which palette each 2x2 metatile uses, despite the fact that only 2 bits are needed. Values are always either $00, $55, $AA, or $FF. This way every 2-bit pair is identical, and you can just mask out the bits you need without having to count shifts and whatnot.


Updating attribute with Horizontal or Vertical scrolling isn't all that difficult -- though it does get a bit more complicated with 4-way scrolling.

I'll see if I can't come up with some dummy code to portray the idea I have in mind. I may edit this post later (or make a new post if I take long enough and someone else replies)

EDIT - bah screw coming up with dummy code. Too much work. If you don't understand what I mean I suppose I could try and clarify more though.

by on (#13199)
I'm working on my engine right now, and I am doing a SMB1 type thing, I'm updating way off screen, well, 32 pixels ahead of the scroll, I update. So each time 32 more pixels are scrolled, I update the next column of attributes off screen. I think I'm going to have a routine, though it's not something you'd usually want to do, where each metatile has a specific attribute number to it:

80 - 0 ;Tiles 80-83
84 - 1 ;Tiles 84-87
88 - 2 ;etc.
8C - 3

Since I already made my level engine, I don't want to go back and change it, because I'm lazy. I would do something like this though:

80 - 0
81 - 1
82 - 2
83 - 3

All the same metatile, but different attribute numbers. The last two bits of the metatile ID keep the attribute number for that metatile. I'd have to change my routine though, and I think this'll do fine. It's a 32k PRG and 8k CHR NROM game, so it's nothing really big.

by on (#13200)
Celius wrote:
I'm working on my engine right now, and I am doing a SMB1 type thing, I'm updating way off screen,


Ah. In that case, yeah, you should be fine with doing it in 32x32 blocks how you have it set up.

In fact -- if you have 32x32 blocks in your level format, you might even want to save time and just have a byte to specify the attribute info for that 32x32 block which is totally independent of the 16x16 metatiles within that block. Just a thought though.

by on (#13203)
I don't get what you're trying to say, sorry. With my method, I'm having my RAM screens (every metatile on NT1+2 is in $300-$4FF) read, and my routine will store attributes on screen according to what is in RAM. So if it sees this:

$80, $84, $xx......
$84, $80, $xx

It will know to store this into the attribute table:

$14

or

0001
0100

Because of what I said about my metatiles, remember? But clarify what you were trying to say, I didn't understand, and I'd like to.

by on (#13211)
Disch wrote:
If you only update every 32 pixels, you will have LARGE color distortion on the edge of the screen as it scrolls (at best, 16 pixels on either end)

Not with horizontal mirroring... You save yourself 128 bytes of RAM just by calculating a whole attribute byte at once and avoiding the creepy read-modify-write thing. I never said the whole byte would use the same pallete. I said he should scan the 4 tile wide column, building each attribute byte, with the individual palettes used by each 16x16 block.

tepples wrote:
Are you doing a 2-screen-tall level (like Super Mario Bros. 3), or are you doing true 4-way scrolling?

The true kind. And I'd say it's more 8-way than 4-way, as nothing stops the screen from moving diagonally. I use 32x32 metatiles, that come with a handy attribute byte ready for use. Except after entering a new name table, vertically speaking, when the metatiles would not be aligned to the attribute table anymore.

I though about ignoring the bottom half of the last row of metatiles of the screens used to build the levels, and have screens of the same height as the NES screen, but that would make level design a bit weird (the metatiles that would be cut would have to be chosen carefully) and collision detection would be pretty funky too, trying to ignore part of the level that was physically supposed to be there...

by on (#13213)
yeah you're right.

If you only have one kind of scrolling and the coresponding mirroring mode setup (such as how SMB, Ice Climber, Balloon Fight, Excitebike are), then you can work as described.

I suppose you only really need to worry if you do 4-way scrolling, or have a scrolling mode which conflicts with the mirroring mode (vert scrolling with vert mirroring, for example).

My mistake. Apparently I was making the situation more complicated than it had to be ^^. I have to remember to KISS

by on (#13215)
Disch wrote:
I suppose you only really need to worry if you do 4-way scrolling, or have a scrolling mode which conflicts with the mirroring mode (vert scrolling with vert mirroring, for example).

Yeah, sure. Updating a full byte of attributes with the setup of SMB3, for example, would look really ugly. I find it ugly enough with 8 pixels of attribute trash.

With 4-way scrolling I guess there is no way to easily calculate the attributes on the fly... Maybe in this case the mirror in RAM is the best choice. I guess with my 32x32 metatiles I could lay the attributes as if they were aligned to the screen, and then shift them all down, pushing everything half a metatile down... doing it to 64 or 128 bytes at a time would be pretty slow, though. Maybe some sort of lookup table? I'm still trying to figure this one out...

by on (#13220)
Disch wrote:
Games often have a whole byte to indicate which palette each 2x2 metatile uses, despite the fact that only 2 bits are needed. Values are always either $00, $55, $AA, or $FF. This way every 2-bit pair is identical, and you can just mask out the bits you need without having to count shifts and whatnot.

Either that, or store them as $00, $01, $02, and $03, so that the engine can just ASL ASL ORA.

by on (#13223)
tepples wrote:
Either that, or store them as $00, $01, $02, and $03, so that the engine can just ASL ASL ORA.


AND+ORA is much simpler than ASL+ASL+ASL+ASL+ORA (between 0-6 ASL's). Shifting a variable amount of times leads to more counting and is ultimately harder to do.

The 00, 55, AA, FF method is suprisingly efficient:

Code:
LDA desired_mask   ; load desired mask
AND tile_attrib    ; mask out desired bits of the tile's attribute
STA temp           ; back up

LDA desired_mask   ; get the mask again
EOR #$FF           ; invert it
AND attribute_byte ; AND with attribute byte
ORA temp           ; plug in our new attribute bits
STA attribute_byte ; and write back


where 'desired_mask' is $03, $0C, $30, or $C0 indicating which attribute bits are to be updated. 'tile_attrib' is the $00, $55, $AA, or $FF attribute assignment for the 16x16 tile. And 'attribute_byte' is the byte in the attribute table that you're changing.

Of course that above code could be optimized and stuff -- it's just to demonstrate the idea.

If you use 00, 01, 02, and 03, shifting the desired number of times will end up being more work. You'll either have to write 4 seperate routines (one to shift 0 times, one to shift twice, one 4 times, and one 6 tiems) -- or you'll have to deal with extra loops to make it dynamic.

by on (#13225)
I used to combine things when my game design used 16x16 metatiles... I stored the attribute of each metatile using only 2 bits, as 6 bits is a lot to waste on repeated data while they could hold other stuff about the metatile. By masking the other 6 bits off, the lower 2 could be use as an index to load a full byte with those 2 repeated from a small table ($00, $55, $AA, $FF), and then I proceeded as Disch said.

It may be a little more trouble than directly loading the full byte (I'm really not gonna waste 75% of a lot of bytes), but is still cleaner than ASL'ing variable ammounts of times.

by on (#13236)
Ah of course! A lookup table! Very good idea. That avoids the shift issue and allows you to use only 2 bits instead of a full byte. Best of both worlds.

by on (#13254)
Disch wrote:
If you use 00, 01, 02, and 03, shifting the desired number of times will end up being more work. You'll either have to write 4 seperate routines (one to shift 0 times, one to shift twice, one 4 times, and one 6 tiems) -- or you'll have to deal with extra loops to make it dynamic.

Unless your attribute update subroutine always reads an attribute-square of tiles from the decoded map in RAM. Then it can always use shift twice. Or are you thinking of read-modify-writing individual bytes in the attribute table?

by on (#13258)
tepples wrote:
Unless your attribute update subroutine always reads an attribute-square of tiles from the decoded map in RAM. Then it can always use shift twice. Or are you thinking of read-modify-writing individual bytes in the attribute table?


The latter. Well... Read/modify/write the RAM copy of the attirbute tables. The acutal table in the PPU would never be read.

I don't see how always shifting twice would work. I mean... eventually you'll have to pack four 2-bit values into a single byte. But even if you could somehow only always shift twice... wouldn't it make more sense to just have $00, $04, $08, $0C instead of $00-$03? That way you wouldn't have to shift at all.

I probably just don't understand your implimentation -- could you explain it more?

by on (#13259)
For my routine, I actually don't use any buffers to keep track of attributes. I take the data in RAM, and store an attribute column depending on the metatile values. I've said this before, I know. I actually have my attributes updated 1 pixel after the metatile columns are updated. If they were updated the same time as my columns, the NMI routine would last way too long. So I'm never doing my Attributes at the same time as my columns, because it takes too long. I have to go, sorry.

by on (#13262)
Disch wrote:
I don't see how always shifting twice would work. I mean... eventually you'll have to pack four 2-bit values into a single byte.

The trick is to always recompute the attributes for all four 16x16 pixel metatiles of an attribute byte at once. This handles the common case (scrolling) quickly and still makes the rarer case (poking tiles) acceptably fast.
  1. Read metatile in lower right corner of this attribute byte from the decompressed map
    Look up attribute
  2. Read metatile in lower left corner
    Look up attribute
    asl, asl, ora
  3. Read metatile in upper right corner
    Look up attribute
    asl, asl, ora
  4. Read metatile in upper left corner
    Look up attribute
    asl, asl, ora
  5. Store attribute byte to attribute copy buffer

by on (#13278)
Yeah, that's great for side scrolling games.

As for vertical scrolling games... I wish there was a simple way to skip the bottom 16 scanlines of a name table, to avoid the half used attribute bytes at all. The trouble of using mapper IRQ's doesn't seem to pay off though...

*Think!*Think!* >_<

There has to be a simpler way of handling attributes... Heck, it doensn't have to be so simple, but it has to make enough sense to be programmed without a bunch of "IF's"... I hate code with lot's of branching. I like to find a general rule, that will work in all cases, even if it makes the one that would be the simplest situation a bit more complicated.

by on (#13283)
Quote:
I like to find a general rule, that will work in all cases, even if it makes the one that would be the simplest situation a bit more complicated.

I 100% agree with that, while as the programmer you don't always have the choise to how you will have to code some stuff.

For attributes tables, the "easier way" to do it is to have a routine that update the color of a given metatile with its coordinates (x;y) and that convert it to attribute coordinate, combine it with already existing bits either by having a software copy of the attribute table, or by reading the real one trough $2007, and finally write the byte to $2007.
Having a table with only 2 bits value seems a dream in the game loop's point of view, and a nightmare in NMI's point of view, also agreeing that it would be a lot more RAM wasted. If you use two nametables of 15x16 nybbles, it will be 480 bytes overall, wich wastes nearly 2 pages of RAM. It may be good escient use, trough, but I think already combining the bytes in a RAM buffer should be do-able in most circonsences.

To read other attributes 2-bits values that aren't modified, if your game isn't too complicated, it is also possible to read the maps 15 metatiles forward/backward if scrolling horizontally and vertically, and 16 metatiles forward/backward if scrolling vetically when the attribute byte is on the scroll lines, and simply read the previous/following meatatile if it the attribute byte is totally scolled. This way may seem complicated, but it actually isn't as long as you only scroll one direction and you're not cheating with a copy of the attribute table in RAM. With more complicated games that merge textboxes and maps such as RPGs, I'd go with the attribute table copy thing, but for a simple game it'd go with that "read backward" method, even if it looks complicated it isn't and it avoid wasting memory / VBlank time.
I'd recommand it for all games scrolling in only one dimension. My game scrolls in both directions and does it that way, but it works with nybbles, and metatiles actually are 32x32, and the byte is splitted in nybbles or not in function of the vertical scrolling.

by on (#13288)
You know, I was thinking of something. How about you have a copy of the attribute table in RAM. Then have $20 bytes in RAM. The $20 bytes of RAM will be used to store whole bytes that represent 2 bits for an attribute. Then when it's necissary, compress the $20 bytes, and store them on screen. The logic is actually simpler than I thought:

1. Decide how you want to designate palletes

ex. Having my metatiles arranged so $80-$83 is one, $84-$87 is another, etc. Then take the last two bits of the metatile ID to see the desired attribute bits.

$82 - Metatile $80, Tiles $80-$83, Attribits - 10
$7F - Metatile $7C, Tiles $7C-$7F, Attribits - 11

2. Store the desired pallete IDs in the $20 bytes of RAM
3. Compress them into $8 bytes
4. Update column/row when neccissary.


Does that make sense?

by on (#13299)
Bregalad wrote:
Having a table with only 2 bits value seems a dream in the game loop's point of view, and a nightmare in NMI's point of view, also agreeing that it would be a lot more RAM wasted. If you use two nametables of 15x16 nybbles, it will be 480 bytes overall, wich wastes nearly 2 pages of RAM.

You need that extra RAM for collision and deformable geometry (e.g. Mario breaking bricks) anyway.

by on (#13305)
tepples wrote:
You need that extra RAM for collision and deformable geometry (e.g. Mario breaking bricks) anyway.

No you don't. At least not for simple broken bricks. In my engine, "breakable brick"-kind of things are treated as objects, and some space in regular RAM is used to define states of all objects in the level. With only 256 bytes, you could define the "alive-dead" state of 2048 objects. That is enough to keep track of a lot of broken blocks, and not loading them again if they are "dead".

However, if you wanted the player to actually move blocks around and remember their position, then you'd need extra RAM.

by on (#28339)
Hi, I'm sorry to bring up this old thread, but it's better than starting a new one for the same thing.

I'm working on my universal scrolling/updating engine, and I have a problem (not a bug). I just have the engine updating the screen by storing columns and rows of attribute blocks (32x32 pixels) every 16 pixels. Since I'm using vertical mirroring, there is large color distortion on the top and bottom of the screen.
The problem is only when scrolling vertically, because there is no horizontal mirroring, so I can have all the color distortion I want off the sides of the screen. So when scrolling vertically, I need to update each row of attributes 16 pixels at a time, keeping the other half that's visible on the other end of the screen the same colors. So I'll need a table in RAM that holds $48 values (Not $40, because sometimes there are 33 tiles being displayed on screen in each row, and I need to have an attribute for that extra tile for every row of attributes.), and I'll need to shift that table a byte left, or right, or 8 bytes left or right (If I press up or down) when I cross 32 pixels.

Does anyone have any ideas on how to either shift the table without taking 800 years, or any other suggestions on how to go about updating attributes 16 pixels at a time scrolling in the direction of the mirroring?

by on (#28353)
16 pixels is the hardware minimum, and split an attribute nybble isn't too bad (you just need to work with 4 bits a time) so you can work with vertical 16 pixel blocks and horizontal 32 pixels blocks. Else, just work with 16x16 blocks everywhere, and when you update row/colums of meatiles, just be sure to refresh just a nibble at the same time (by keeping a copy of the attribute table in RAM for example).

by on (#28366)
That was my plan all along. But take a look at this:

$00,$55,$AA,$FF,$1B,$09,$30,$44,$33

This represents a 9 byte long row of attributes. It's 9 bytes, because if you scroll right 4 pixels from the very beggining nametable, You'll be seeing half of the tile at $2000 and half of the tile at $2400, as well as all of them in between. So there are 33 different tiles on screen at once, and I need to have the attribute information for the extra tile on the edge of the screen. But anyways, when I scroll 32 pixels to the right, this row will need to changed. It needs to shift one whole byte over. So $00 at the beggining will be sent to the garbage, and an extra byte will have to be added at the end. Say the attribute byte for the next column is $EF. I need to then add that after $33. So it will look like this:

$55,$AA,$FF,$1B,$09,$30,$44,$33,$EF

This is an easy concept, but to do, it would take a very long time. Does anyone know of a way I could do this without taking a very long time?

by on (#28367)
How long is a "very long time"?

What you are describing involves shifting a 72-byte buffer by a distance of 1 byte. The following takes fewer than 1100 cycles per my count, and would take even fewer if unrolled:

Code:
  ldx #0
@loop:
  lda buffer+1,x
  sta buffer,x
  inx
  cpx #71
  bcc @loop

by on (#28372)
If you need 72 bytes for this complicated scheme to work, why not just go all the way and mirror the whole attribute tables and use 128 bytes? It's not that much more, and will keep you from having this sliding buffer.

But if you do want to stick to that "shifting" scheme, that can usually be done by not shifting at all, but having a pointer indicate which entry represents the leftmost column.

From your example, this variable would start as "0". But once you scrolled 32 pixels, the $00 is not the first any more, $55 is. So, instead of shifting the whole thing left, have the variable move right, incrementing it to 1. Of course, this changes the way you access that buffer. Instead of counting X bytes from the start of the row, you count X bytes from what the variable indicates is the first byte in the row. Continuing the example, the row now starts at position 1, and you have to update the byte at the other edge, 8 bytes later. So you add 8 to the variable (which holds 1) and you get 9. Since this value is beyond your range (0 to 8), you subtract 9 from it, and the result is 0, which is indeed the last byte in the row now. So you replace the $55 in there with the new $EF.

In fact, since you know that last byte of the row is the one located before the first, it'd be better to subtract one from the variable instead of adding 8. The range check would be simpler, as you would just need to check if the result was negative, in which case you make it wrap to 8. The effect is exactly the same.

The only complicated part is that now some math is required to calculate the exact index of the byte in the row you haveto change, but still much less than sifting that number of bytes.

by on (#28381)
tokumaru wrote:
If you need 72 bytes for this complicated scheme to work, why not just go all the way and mirror the whole attribute tables and use 128 bytes? It's not that much more, and will keep you from having this sliding buffer.


You know, that's not a bad idea. I could really benefit from this, and you're right, I wouldn't have to deal with the shifting thing. I'd already have all the information I'd need right in the table.

tokumaru wrote:
The only complicated part is that now some math is required to calculate the exact index of the byte in the row you haveto change, but still much less than sifting that number of bytes.


Math is no problem for me. Everything in my scrolling/updating engine is calculated using the value of the scroll variables, or distorted scroll values. Calculating the index will be very easy, I think. But that's a very good idea to have the 2 attribute tables both in RAM. I think I'll have each row in RAM be 2 side by side rows of attributes. So $200-$207 is $23C0-$23C8, and $208-20F is $27C0-$23C8. That will make it very much easier.

EDIT: I didn't see your post until now Tepples. My shift wouldn't have been that simple. Because if you just do that, what will happen is the attribute which would go to $23C8, which is at the left side of the screen, would be shifted to $23C7, which obviously is something someone would not generally want. I think I'll just stay away from shifts though.

by on (#28649)
I'm planning my scrolling engine out, and I've also run into an attribute issue.

If I'm scrolling to the right, the attribute data has to be written in a column from top to bottom. Now, the issue I'm having is that it doesn't matter whether I have a copy of the attribute data in the ram or whether I just grab it from $2007, when updating a column of attribute data, I need to write to every 8th byte, and there's really no good way to do this other than resetting $2006 for each write, whereas updating a row is simply just write write write write 'kay done.

And I'm doing 8-way scrolling, "rolling" with both axes, using horizontal mirroring (like Kirby's Adventure).

But yeah, is there any more of an efficient way to update a column of attribute data than to just reset $2006 for each write? (by resetting, I mean manually incrementing the address by $08, and rewriting the incremented value)

by on (#28651)
Drag wrote:
But yeah, is there any more of an efficient way to update a column of attribute data than to just reset $2006 for each write? (by resetting, I mean manually incrementing the address by $08, and rewriting the incremented value)

I don't think so. I also got very upset because of this a while ago... it would be useful if the PPU had an "increment 8" mode, as it has "increment 1" and "increment 32". I don't think thisis such a problem though, because it's just so little data... you'll most likely update only 9 bytes, so the overhead of setting the address before each write is not so bad.

At one point I even though about coding a very efficient routine to copy the whole 128 bytes of the attribute tables mirror every frame, but now I realize that would be insane.

by on (#28652)
Drag wrote:
I need to write to every 8th byte, and there's really no good way to do this other than resetting $2006 for each write


Use the increment 32 mode anyway, just interleave the tiles. 4 passes will be just like update 8.

by on (#28653)
That's a good idea, actually. I'd still need to play around with the pointers used to access the attribute data in the ram (if I decide to put it in the ram).

It's probably a better idea to have the attribute in the ram for me, because I have a status bar, and the way it works is that the two nametables are the same, except one of them (the one on the bottom) contains the graphics for the status bar, so every write I make to the PPU would need to be doubled to the other nametable, while making sure not to overwrite the status bar.

I don't think this is inefficient, because Gargoyle's Quest II does this exact thing, exactly the way I was going to do it (Kirby's Adventure mirrors the screen with a reduced height, whereas Gargoyle's Quest II just simply mirrors the two nametables, without disturbing the status bar on the one nametable)

Thanks for the help! :D