Perhaps someone should mock up a specification for a CPLD that only provides 8x8 attributes and nothing else.
So
something (static ver.) that did basically this:
New version with different attribute organisation,
moment while I make a new graphic.
Yeah, I was about to yell at you with the previous organization =P (although I think that it being big endian instead of little endian may be better, but this is still more manageable anyway)
The original organization is "free" because it retains the PPU's existing subdivision. The nicer organization requires something to shove bits around ... which is why UNL_DripGame presents its interface to 8x8 attributes as a 2048x2-bit array in CPU memory. (Does the convenience of "no bitpacking" outweigh the "takes four times as long"?)
I do not understand this picture. Attribute table RAM is normally $40 bytes long.
Personally if I'd have to do a circuit that does *only* 8x8 (or any finer than 16x16) attribute size, I believe it should be possible to do with discrete logic (and maybe PAL), by sacrifying one name table in CIRAM, and use the other half of CIRAM for colour data. Unfortunately it's *not* possible to highack the adress lines of CIRAM, making such a trick hard to realize. The answer is to steal a part of CHR-RAM, whose address lines *can* be highacked, by the second 1k of CIRAM.
So it'd work like that :
* Name-table : 1k of CIRAM (minus $3c0-$3ff, unused)
* Finer-grained attribute-table : 1k of CHR-RAM (reads like a name, table)
* Pattern tables : 7k stands in physical CHR-RAM, and 1k in the second half of CIRAM, but nobody notices, and this fakes 8k of CHR-RAM
A register traps NT fetched, and uses it's adress to replace adress lines of CHR-RAM on AT fetches... that's it. Not very simple, but not very complicated either. Oh, and all the content of the new attribute table should be repeated 4 times in a byte.
Nice idea, Bregalad! It just sounds like a waste of space to dedicate the an entire 1KB to attributes, while all you really need is 240 bytes. Couldn't we use the rest of this memory as a name table for status bars or something?
I touched on this briefly when
I was working on my own hypothetical mapper.
You take advantage of the fact that the currently-needed attribute fetching address is derived from the nametable fetch that occurs immediately beforehand, so that's the first fetch with ppuA13 high. You just remember the address from that fetch (using a latch or something), and then you rearrange the bits to form a new attribute address to fetch. My design arranged the expanded attribute table to be like the regular attribute table, but with 8x8 regions instead of 16x16 (so each byte described a 16x16 region of four 8x8 regions).
And then after that, my design was to extract the two bits of interest from the extended attribute byte, repeating them 4 times to form a full byte, since the NES is still expecting its own attribute format and will select two bits out of the byte on its own, mirroring the bits just removes the need to figure out which two bits the NES is going to extract from the byte you give it.
It would probably be simple to just mirror the bits in the software that fills the attribute table, if you wanted to avoid spending hardware on extra data line mirroring logic.
Spending 1k on attributes instead of packing them sounds sensible enough to me, if it simplifies the hardware. You're probably not hurting for CHR-RAM size these days.
I thought I addressed most of this in the linked page in my userspace. Did people not click through?Bregalad wrote:
I do not understand this picture. Attribute table RAM is normally $40 bytes long.
I seem to have neglected to precisely mention the actual "add a RAM chip to the cart to handle attributes" anywhere, aside from the //then cart-internals: first, the (probably 6264) But since the [added] RAM was only being used for Attribute Tables, not CHR, I called it ATRAM.
Bregalad wrote:
Unfortunately it's *not* possible to highack the adress lines of CIRAM,
I did note/mention that...Bregalad wrote:
A register traps NT fetched, and uses it's adress to replace adress lines of CHR-RAM on AT fetches... that's it. Not very simple, but not very complicated either. Oh, and all the content of the new attribute table should be repeated 4 times in a byte.
That, however, is pretty close to exactly what the Verilog I wrote on the
linked page in my userspace does. (It also remaps more of PPU-address-space as a write port.) It stores each attribute byte as packed, though, and just mirrors the relevant two-bits across the byte when read based on the preceding NT-fetch...like what Drag said.
[hr]
Mind, I didn't actually design/code the actual PRG- and CHR-mapping portions that need taking care of. I'm told that 1,2,4kiB SRAMs are no longer cheaply available, just 8kiB and 32kiB. The 4-screen AT is ~1kiB. 4-screen NT (that is, adding 2 screens' worth) is ~2kiB. CHR-RAM unbanked is 8kiB, so that's not nice to add without adding banking...
e: So what sort of CHR-banking (and PRG, I suppose) would be sensible, as it seems like "just" adding 8x8 attributes seems like a waste of CPLD?
I've figured out a simple "CNRAM" style discrete logic 8x8 attributes in 5 ICs (
nesdevwiki:User:Lidnariq/Mapper thoughts#8×8 attributes).
For the programmer, it'd be four 8 KiB pages of CHR-RAM, where tile #s $3C-$3F, $7C-$7F, $BC-$BF, and $FC-$FF in the left pattern table is replaced with the replacement attribute tables. Each of the four pages would correspond to the four corners of each 16x16 zone.
But this is the layout that Sik was complaining about, so maybe it's a bit too annoying.
Oops, I saw the image and skipped right over the link. Sorry.
It looks like we had the same idea, so I don't have anything else to add, other than I tried a couple of layouts for the bitpacking and settled on the "like the PPU except 8x8" because it was already pretty versatile for both horizontal and vertical nametable updates.
I think Tepples might've mentioned the CPLD so there'd be an implementation of 8x8 attribute tables for others to use in their CPLD mappers, since 8x8 attributes doesn't really provide any advantages or disadvantages for any particular chr/prg banking style.
tokumaru wrote:
Nice idea, Bregalad! It just sounds like a waste of space to dedicate the an entire 1KB to attributes, while all you really need is 240 bytes. Couldn't we use the rest of this memory as a name table for status bars or something?
(moved from split topic)
"...per nametable upgraded. (And technically, 180 bytes, if you let CIRAM take some, but then you have less arrangement control.)"
Quote:
since 8x8 attributes doesn't really provide any advantages or disadvantages for any particular chr/prg banking style.
Quote:
like a waste of CPLD
okay, more like a waste of a 6264 (8kiB)...
There's already address lines attached to nice-arranged (present revision) to allow PPU-space mapped registers, at 14'b__11_xx01_RRRx_xxRR, instead of in CPU-space, but that would prevent raster effects from being as easy or as nice.
It occurs to me that one could implement 8x16 attributes with a little discrete logic: a flip-flop to toggle even/odd for each AT fetch, hooked up (with an AND to mask it to only AT area) to CIRAM /A10, so that the second nametable's attribute zone gets co-opted for the other half of the first. [Like my other "simple" suggestions, this is hellish to write to; restricting the oscillating behavior to reads with another input on the AND, and allowing PPUA10 to control CIRAMA10 for writes, would make it somewhat easier at the cost of somewhat more logic...but maybe you could fit it all in one NAND chip.]
16x8 attributes would be harder, since you need to implement a scanline counter and then switch every 8.
Of course, were the cartridge to have access to four more address lines, one would have just enough to maximize the first nametable's attribute density at 8x1, putting it in the other table. But that's just wishful thinking.
For either 8x16 or 16x8 attributes you may as well just latch A0 or A5 on the nametable fetch.
I was pondering how to make hexagon-arranged square attributes (think how Koei's strategy games often do it)--but then I realized that just doing a 16x8 and having some mild redundancy in attribute tables would be far, far, easier than actually implementing 16x16 offset attributes (check row, check column, selecting the proper attribute out of the byte, adding to the row...) along with being more flexible for the rest of the screen.
I have been contemplating porting Hex Frvr to the NES for a while, using 8x16 attributes.
8x16 attributes have the advantage that they can just be crammed into the two on-board nametables, making it famiclone-compatible. They're not quite practical to do just in discrete logic, but it does fit quite nicely into a GreenPAK.
lidnariq wrote:
famiclone-compatible.
Is that because cart-edge CIRAM /CE isn't respected?
Yeah, that.
It also imposes a nice tight constraint on not overdesigning.
Oh, another thought: interrupts (or timed code) and raster effects are an easy way to get 16x8 attribute zones. Albeit at the cost of making the nametable layout suck as well.
lidnariq wrote:
Oh, another thought: interrupts (or timed code) and raster effects are an easy way to get 16x8 attribute zones. Albeit at the cost of making the nametable layout suck as well.
The lower playfield in
Klax uses this.
It seems that non-minimal versions, including the "nice" arrangement in post 2, or the nicer arrangement of being in PPU_ are not workable on PowerPak (at least, not if one is using CHR chip, doing it onboard still works just fine…but then why not finish the ExRAM featureset with CPU-bus access?) because it does not interpose CHR data[7:0] or CHR address [9:3].
On the other hand, neither does the Game Genie interpose CPU data. But does the PowerPak have bus conflict-prevention resistors?
In a less-inspired tone, even more "minimal" 8x8 attributes by using two bits of nametable would be in discrete range: two gated latches to store on NT fetch and output on AT fetch, decoding and some bus-conflict prevention circuitry- a 4x2:1 mux?
Code:
wire at = (& PPU_ADDR[9:6]);
wire CIRAM_CE = PPU_A13 & ~at;
wire LATCH_E = PPU_A13 & ~at & PPU_RD;
wire MUX_LATCH = PPU_A13 & at & PPU_RD;
//note I've not used the active lows
Fitting that logic onto two non-mux chips is left as an exercise for the reader.Code:
wire CIRAM_CE_n = NOR (at,A13_n);
wire MUX_LATCH_SELECT = AND(at,NOR(A13_n,RD_n));//7421 7427
wire LATCH_E = NOR(at, A13_n, RD_n);
e: only possible with CHR-ROM.