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

Writing pattern tables / tile data

Writing pattern tables / tile data
by on (#41310)
Now I'm following NES 101 to make some graphics.

I managed to get the palette info on VRAM.
But then I try to write some tiles to VRAM $1000 and then read them back to test if all went ok, the following happens:
    - on most emulators, only zeros come out.
    - on Jnes the info that was written appears when read, but shifted one byte to the right. The sprite doesn't show.
    - on my PMP with NES emulation, the same info than in Jnes is read, but here the sprite shows perfectly!

I'm trying to stick the Tile data this way, as I'm coding in C and cannot include it in CHR-ROM with things like .incbin .org .bank .advance, etc.
So there are two ways of doing it, in CHR-ROM and in VRAM, right?

The CHR info I (try to) write to $1000 VRAM:
Code:
byte tiledata[] = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //   1st tile: blank (16 bytes)
0x18,0x24,0x66,0x99,0x99,0x66,0x24,0x18,
0x00,0x18,0x18,0x66,0x66,0x18,0x18,0x00  // 2nd tile: diamond ( another 16 bytes )
};


The data read back from VRAM $1000: ( on Jnes and my PMP )
Code:
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x18,0x24,0x66,0x99,0x99,0x66,0x24, // note that this is the start of the 2nd tile,
0x18,0x00,0x18,0x18,0x66,0x66,0x18,0x18  // but shifted 1 byte to the right


Program:
Code:
 ...
   #define sizeof_tiledata   32
   unsigned char i;
   unsigned char *p;
   
   // write 2 tiles to VRAM ...
   addr(0x2006) = 0x10;
   addr(0x2006) = 0x00;
   for( i=0, p=tiledata; i!=sizeof_tiledata; ++i, ++p )
      addr(0x2007) = *p; // write 2 tiles, 16 bytes each
   
   // ... Read them back
   for( i=0; i!=sizeof_tiledata; ++i )
   {
      addr(0x2006) = 0x10;
      addr(0x2006) = i;
      cprintf("%02X.", addr(0x2007) );
   }


Any clues?
Thanks in advance!

PS: If I haven't explained myself enough, please ask.

by on (#41313)
The reason it's shifting to the right is because the PPU reads are buffered. After you change the PPU address, discard the first $2007 read, and that's normal.

by on (#41315)
Thanks! I just missed that in the NES docs.

Well, then after the code I pasted above, there's this:
Code:
   // load sprite 0 data
   addr(0x2003) = 0;    // sprite index
   addr(0x2004) = 10;   // sprite x
   addr(0x2004) = 1;    // tile index
   addr(0x2004) = 0;    // attributes
   addr(0x2004) = 10;   // sprite y
      
   // enable graphics
   addr(0x2000) = bin(1,0,0,0,1,0,0,0);
   addr(0x2001) = bin(0,0,0,1,1,1,1,0);


So this works in my PMP showing the sprite at 10,10. But not on emulators.
I haven't set any background tiles or name tables.
Should this happen this way? I know my PMP's emulation isn't sure 100% accurate. But what is missing here in order to make the sprite show?
The sprite shows and has the same colors as in the NES 101 demo, so palette data must be ok.


PS: I waited two VBlanks at the begining, and also disabled graphics by a zero write to both $2000 and $2001.
PS2: the bin() macro should be self explanatory

by on (#41318)
Writes to CHR-RAM only work if you have CHR-RAM. If your game has CHR-ROM, good emulators (read: not JNES*) will ignore those writes to the pattern tables because you can't write to ROM (it is "read only memory" after all). This could explain why only JNES works and none of the other emulators. And I don't really know what PMP is, so I can't say anything about that.

As for why your sprite isn't showing...

1) you're turning on BG rendering with that $2001 write -- but are you clearing the nametable? If you didn't, there might be solid garbage tiles being drawn which might be masking or hiding the sprite. If you're working on just sprites and no BG, don't enable the BG yet -- just enable sprites.

2) did you set up the palettes? Maybe the sprite is being drawn but is invisible because it has all the same colors as the background.

3) instead of using $2003 and $2004 (why oh why do tutorials teach $2004!). Look into $4014. This isn't really your problem but I feel I should mention it. $2004 is useless in most practical applications.

4) could be the same confusion with CHR (pattern tables). Are you sure tile 1 is the graphic you want? Look at the pattern tables in a any emulator with debugging tools (like FCEUXD) to make sure they're what you expect. The sprite may be drawing but tile 1 may be totally blank.



* jnes footnote/rant: I never understood why jnes was so popular. I mean it was a mediocre emulator even when it first came out... yet people were drawn to it over all the other superior alternatives. *shrug*

by on (#41321)
Sorry, PMP is portable media player, it's an MP4 player with NES ( not 100% accurate ) emulation.

SO How do I decide to have or not to have CHR-RAM ?

1) ok, I'll try that.
2) Yes I did, and on the PMP the sprite shows with the same colours of the demo where I copied the code, so I'm pretty sure the palette data is ok.
3) Yes, I'm aware of the $4014 preference. It's just that seeing $2003 work fine in the PMP made me lazy to try $4014, but I will. I swear I will.
But what I don't fully understand is why is copying 64 sprites data each vblank preferred even when you are just using a couple? isn't that a waste of processor cycles?
4) Well at least I think so. reading back the tile data at VRAM $1000 yields the same I wrote to it, and the tile shows perfectly in the PMP without modifying the code, it may not be the best emulator, but it's not making up the tile data.

If anyone has some experience with cc65, is there a way to link with compiled asm code that puts binary data directly into ROM, like CHR-ROM?

Thanks Disch!

by on (#41327)
Petruza wrote:
SO How do I decide to have or not to have CHR-RAM ?


Generally, you either only have CHR-ROM, or CHR-RAM. Only in very special circumstances (with specific mappers) can you have both. So check the .nes file you're producing. You can either open it up in a hex editor and check offset 0x00005. Or you can open the ROM up in an emulator which displays stats about the ROM (in FCEUXD, after you open up the ROM, go to Help | Message Log and it should say "CHR-ROM: N x 8Kib" where 'N' is the number of CHR-ROM pages (same a the header).

In either case... zero = CHR-RAM, and anything nonzero = CHR-ROM.


3) Well for starters you need to copy all 64 sprites at least once so that you can ensure the sprites you aren't using are hidden (by moving them offscreen). If you don't do this, they may appear as blocks of garbage on the screen.

- Plus, $2004 isn't as quick as you might think. LDA abs, STA abs (a typical write) is 8 cycles total. 4 bytes per sprite = 32 cycles to draw a single sprite. Which means 16 sprites will take 512 cycles (512 cycles is how long the DMA takes). So if you have under 16 sprites, yeah it might be a little faster... but otherwise it isn't. And see next point

- Plus, it's a speed priority issue. Organizing and drawing all the sprites can eat precious VBlank time... whereas if you organze and draw to "shadow oam" during frame rendering and do the full DMA during VBlank, you may end up using more time overall, but will use less VBlank time.

- Also there's this whole other thing with OAM decay (which emulators generally don't emulate) where you either need to keep sprites drawn, or keep refreshing OAM or else the memory "decays" and the values change (which could result in phantom garbage sprites appearing out of nowhere).

4) I really suspect this is your problem, actually. In an earlier post you said that when you tried to read the tiles from $1000... "on most emulators, only zeros come out. ". That tells me that $1000 is totally blank.

Did you look at a debugger? Seriously -- if you don't have FCEUXD, get it. Load the ROM, and go to Tools | PPU Viewer. Make sure the tile graphics are exactly what you expected them to be. You'll be surprised how much easier debugging is when you use a debugger.

by on (#41336)
Well now I see the $5 offset is 1, CHR-ROM, that's why good emulators return zeros.
Ok I'll have to find out how to control the iNES header, or how to put info to ROM in c.


Ok thanks for the explanation on $4014. You totally convinced me.

Yes I tried FCEUXD and tiles don't show up, but now I know that that's because of the CHR data being in rom, not in ram.

Thanks!

Rant: People here only code in asm. People on cc65 mailing list don't like or know too much of the NES platform... I'm caught in the middle :'-(
Doesn't matter, I'll get it working sometime.

Update: manually changing the iNES header to set CHR-ROM blocks to zero made the tile show on FCE and other emus. :D

by on (#41341)
Petruza wrote:
Ok I'll have to find out how to control the iNES header, or how to put info to ROM in c.


I can't really be much help here without seeing your build settings. Probably, though, the route you'd want to go with stick with CHR-ROM. CHR-ROM is generally easier since you don't have to draw the tiles to CHR-RAM (they're already there), plus then the CHR doesn't use PRG space.

cc65 is probably including some external file or something to use for the CHR. Maybe check the .cfg file (if there is one) to see if it lists one. I wish I could be more help with this, but I've only really used the assembler+linker in the cc65 package. Never actually made anything in C with it.

Quote:
Rant: People here only code in asm. People on cc65 mailing list don't like or know too much of the NES platform... I'm caught in the middle :'-(
Doesn't matter, I'll get it working sometime.


My sympathies. I'm curious to see how well it works out, honestly. I always thought C would be too high level to have practical application on the NES, but you never know. I'm really glad you're trying it out and sticking with it. Maybe when you're done you can write like a FAQ or tutorial or something. :wink:

Best of luck!

by on (#41346)
Disch wrote:
I always thought C would be too high level to have practical application on the NES

Interpreted floating-point BASIC is even slower than compiled C. Yet Oregon Trail was "fast enough" on the Apple II Plus despite being written in BASIC with a few assembly language subroutines to handle blitting. Not all games are as graphically intense as, say, Recca.

by on (#41350)
Disch wrote:
My sympathies. I'm curious to see how well it works out, honestly. I always thought C would be too high level to have practical application on the NES, but you never know. I'm really glad you're trying it out and sticking with it. Maybe when you're done you can write like a FAQ or tutorial or something. :wink:

Best of luck!


Thanks! I will. And thanks to you all for the help.

The c NES library I'm writing will be for simple demos, hello-worlds and basic games. But I'm sure it will be able to do pretty good things, though.

by on (#41356)
That's fun, but I recently tough about coding for the NES in C.

And I tried to look at CC65, and honnestly, the way the libraries are set up discouraged me quickly. There is a incredible number of small asm files that are a complete mess and you have to deal with many shattered files and makefiles which are a real pain in the ass (to me at least, some more experienced C programmers may not find that bothersome).

I think it should be perfectly possible to write decent programms in C, as long as the time critical stuff remains in assembly (aka interrupts).

NMI and IRQ should be written in assembly because they need to update data to the PPU in a time-critical operaion, and in addition to this they should save the A, X and Y registers and resore them, something I don't think is possible in C (without using asm statements).
Reset should be written in assembly to clear memory, initialise the C argument stack (as well as the hardware stack) and call the C main() function.

I think libraries should be rewritten, and that with in mind that they are not used for a console-based computer like most C compiler are, but on a video game console that is supposed to show graphics (and not plain text), and that commands such as printf() or usual stuff like that makes absolutely no sence in this situation. Other funcitons that make sense for the NES, such as writing a string to a buffer that will be updated on the PPU on the next VBlank, should replace the standard functions.
Even if this implies making a version of C wich is not standard, anyway it isn't because float, double and boolean types are not supported at all by CC65.

If only CC65 could optionally have a stack that is acceded with "stack,X" instead of "(stackptr),Y", this would be much more optimised, even if it would limit the size of the stack, I think 256 bytes would be enough for something decent (if static variables are extensively used). Also, if it would be possible to retun booleans via the C flag, it would be awesome (I always do that in assembly).

by on (#41360)
There was a version of Contiki that compiles with cc65. The PPU is broken in the library, and it displayed OK on certain emulators. http://www.sics.se/contiki/

That's what I was hoping to fix when I was messing with it.

by on (#41442)
Yay! I managed to compile the nes library for cc65 with CHR-RAM instead of ROM :D Happy, happy me :!:

by on (#41447)
That's great news. The more I think about it, the more I guess it would be really cool to programm the NES in C. Altough you'd have to get a very optimised compiler.

by on (#41454)
I think the best would be having a library written in assembler with time-critical routines, and have the c header files to use that routines, and make the application body in C.

by on (#41456)
Pretty much. CC65 allows assembly and C routines to call eachother (in theory, I haven't tried).
Libraries in assembly should include PPU writes (and possibly reads) handling and maybe raster effects handling, reset routine (of course), and the usuall stuff to do math. With that I guess it should be possible to write something in C.

by on (#41457)
Bregalad wrote:
Pretty much. CC65 allows assembly and C routines to call eachother (in theory, I haven't tried).


Totally. I have. As long as you have the object file ( .o ) and the API definition ( .h for C or .inc for assembler ) it doesn't matter in which language it was written, you can use it from both languages.

Quote:
Libraries in assembly should include PPU writes (and possibly reads) handling and maybe raster effects handling, reset routine (of course), and the usuall stuff to do math. With that I guess it should be possible to write something in C.


Exactly! and also sound routines, joypad handling, timing, and reading and writing from and to Save RAM.

by on (#41458)
I agree for joypad and sound (a sound engine is called each frame so it better has to be efficient).
However, why write and read from SRAM should be done in assembly ? I don't see the problem of doing it in C. Maybe at the critical time while saving, but this is a matter of changing a few pointers if done correctly I guess (altough I haven't done any game that saves yet).

by on (#41460)
You're right, I was just naming which functions should be included in a NES library, some of them written in assembler, some in c, doesn't matter.

by on (#41721)
My c NES library work in progress:

Image

Yes, I know it looks a little bit gay , but I was just testing sprite DMA with random values and the rainbow colors popped out :D