nesdev.com
http://forums.nesdev.com/

[SOLVED]Trying to display all tiles of CHR on screen
http://forums.nesdev.com/viewtopic.php?f=10&t=18012
Page 1 of 2

Author:  Haruka [ Wed Nov 07, 2018 5:55 pm ]
Post subject:  [SOLVED]Trying to display all tiles of CHR on screen

Hello everyone. I'm trying to display all tiles of CHR on the screen that an NROM can hold.

Here is the CHR I've made:
Attachment:
0000.png
0000.png [ 24.86 KiB | Viewed 3255 times ]


Here is the code I've written:
Code:
#define PPUCTRL   (*(unsigned char*)0x2000)
#define PPUMASK   (*(unsigned char*)0x2001)
#define PPUSTAT   (*(unsigned char*)0x2002)
#define PPUADDR   (*(unsigned char*)0x2006)
#define PPUDATA   (*(unsigned char*)0x2007)


static const unsigned char bg_palette[16] = {0x0F, 0x00, 0x10, 0x20,   0x01, 0x11, 0x21, 0x31,      0x05, 0x15, 0x25, 0x35,      0x0A, 0x1A, 0x2A, 0x3A};
static const unsigned char spr_palette[16] = {0x0F, 0x13, 0x23, 0x33,   0x01, 0x71, 0x27, 0x37,      0x05, 0x1C, 0x2C, 0x3C,      0x0A, 0x19, 0x29, 0x39};


void isr_NMI(void);


static void waitForVblank(void){
   while((PPUSTAT & 0x80) != 0);
}
 
static void drawScreen_C(void){
   unsigned char i = 0, j = 0;
   unsigned char idx = 0;
   for(i=0;i<16;++i){
      unsigned short addr = (unsigned short)(0x2000 + i * 32);
      PPUCTRL = (unsigned char)0x80;
      PPUADDR = (unsigned char)((addr >> 8) & 0xFF);
      PPUADDR = (unsigned char)((addr >> 0) & 0xFF);
      for(j=0;j<16;++j){
         PPUDATA = idx++;
      }
   }
   idx = 0;
   for(i=16;i<30;++i){
      unsigned short addr = (unsigned short)(0x2000 + i * 32);
      PPUCTRL = (unsigned char)0x90;
      PPUADDR = (unsigned char)((addr >> 8) & 0xFF);
      PPUADDR = (unsigned char)((addr >> 0) & 0xFF);
      for(j=0;j<16;++j){
         PPUDATA = idx++;
      }
   }
}

void isr_NMI(){
   //drawScreen_C();
}

void main(void){
   unsigned char i;

   // enable vblank
   PPUCTRL = (unsigned char)0x80;

   // change palette during vblank
   waitForVblank();

   // set palette
   PPUADDR = (unsigned char)0x3F;
   PPUADDR = (unsigned char)0x00;
   for (i = 0; i < 16; ++i){
      PPUDATA = *(bg_palette + i);
   }
   for (i = 0; i < 16; ++i){
      PPUDATA = *(spr_palette + i);
   }

   drawScreen_C();
   
   // turn on rendering
   PPUMASK = (unsigned char)0x0E;

   while(1){
      
   }
}


What I expected:
Attachment:
0001.png
0001.png [ 29.85 KiB | Viewed 3250 times ]


What I finally got:
Attachment:
0002.png
0002.png [ 31.27 KiB | Viewed 3250 times ]


Could anybody give me any hint and tell me how to achieve my goal? Thank you!

Author:  Banshaku [ Wed Nov 07, 2018 6:23 pm ]
Post subject:  Re: Trying to display all tiles of CHR on screen

You cannot show the content of both pattern table at the same time: one is for Background and one is for sprites.

You can only show the content of one pattern table for the BG, since 1 pattern table has 256 values and those index refer to the currently selected pattern table. This means your program is working properly, since your first write the BG data, it shows the content of the first pattern table, then you switched pattern table, now the BG is updated to show the content of the newly selected pattern table, then you writing index 0-256, which show the content of the currently selected pattern table. So both will show the content of the selected pattern table, which is the expected behavior.

Author:  rainwarrior [ Wed Nov 07, 2018 7:39 pm ]
Post subject:  Re: Trying to display all tiles of CHR on screen

You could do a sprite 0 split halfway through the screen to change the tiles below a given scanline, but the easier way might be just to hook up bit 4 of $2000 to a controller button so you can flip between the 2 pages easily.

Edit: Just to clarify, that bit does not affect what value you write to the nametable backgrounds. I notice you flip it when you write the second half. The big only affects what is displayed at time of rendering, it doesn't affect the stored nametable data at all.

Author:  Haruka [ Wed Nov 07, 2018 8:58 pm ]
Post subject:  Re: Trying to display all tiles of CHR on screen

rainwarrior wrote:
You could do a sprite 0 split halfway through the screen to change the tiles below a given scanline

Well, I believe that is indeed something advanced technique. I'll continue reading the tutorials and trying my best to see if I can make it.

rainwarrior wrote:
but the easier way might be just to hook up bit 4 of $2000 to a controller button so you can flip between the 2 pages easily.

Actually displaying the 2 pages of pattern table is not my final goal.
I'm recently planning to make an interesting cartridge that contains a powerful MCU on board, which generates images in realtime, and converts the image data to "pattern data format of NES PPU" and finally flush the data chunk to CHR RAM every NMI. The MCU does nearly all the work (almost instantly compared to NES itself due to its strong power and fast speed), the NES only needs to display the 2 pages of pattern table on the screen with a well organized nametable.
Now you see, I encounterd the problem.
That's the whole story.

Author:  Banshaku [ Wed Nov 07, 2018 9:11 pm ]
Post subject:  Re: Trying to display all tiles of CHR on screen

With a memory controller like an mmc3, what you would do is to change the content of the chr bank at a specific location and it would allow to continue to show the content of another bank. With the scanline counter and proper timed code, it wouldn't be an issue. This is how you would display a pictures that exceed the amount of 256 tiles per pattern table.

Author:  Haruka [ Wed Nov 07, 2018 9:34 pm ]
Post subject:  Re: Trying to display all tiles of CHR on screen

MMC3 is a good idea. But currently I don't know how to manipulate MMC3. Maybe I could try searching some sample code.

Author:  rainwarrior [ Wed Nov 07, 2018 9:36 pm ]
Post subject:  Re: Trying to display all tiles of CHR on screen

If you're planning on running all the code in a separate CPU you could just make the NES NOP for half the frame and then write to $2000 to switch the tiles at that point, no need for even an IRQ or waiting on a sprite.

Author:  tokumaru [ Wed Nov 07, 2018 10:41 pm ]
Post subject:  Re: Trying to display all tiles of CHR on screen

rainwarrior wrote:
If you're planning on running all the code in a separate CPU you could just make the NES NOP for half the frame and then write to $2000 to switch the tiles at that point, no need for even an IRQ or waiting on a sprite.

But you still have to sync with the end of vblank in order to do this, unless the NMI handler executes in a constant number of cycles. The sprite 0 hit flag and the sprite overflow flag can be used to detect the end of vblank, since that's when those flags get cleared.

Here's a question: if you're generating the patterns externally, why are you restricting yourself to using only 512 tiles? If you use 16KB of CHR-RAM, instead of 8KB, you'll have enough tiles to fill the entire screen with unique graphics. The only problem is that the NES can only address 8KB by itself, you'd need a mapper (something as simple/cheap as CNROM will do) to switch the rest of the CHR-RAM.

Author:  Haruka [ Thu Nov 08, 2018 1:21 am ]
Post subject:  Re: Trying to display all tiles of CHR on screen

tokumaru wrote:
Here's a question: if you're generating the patterns externally, why are you restricting yourself to using only 512 tiles?

Because 8KB of CHR RAM is enough for my requirement.

tokumaru wrote:
If you use 16KB of CHR-RAM, instead of 8KB, you'll have enough tiles to fill the entire screen with unique graphics. The only problem is that the NES can only address 8KB by itself, you'd need a mapper (something as simple/cheap as CNROM will do) to switch the rest of the CHR-RAM.

Yes, exactly. however my CNROM cartrigde does not work at all. Once powered on, the CHR ROM or CHR RAM immediately got hot. I believe there must be short-circuit somewhere when I replaced the mask rom chips to flash/sram from my donor cart.

Author:  Bregalad [ Thu Nov 08, 2018 9:27 am ]
Post subject:  Re: Trying to display all tiles of CHR on screen

Quote:
With a memory controller like an mmc3, what you would do is to change the content of the chr bank at a specific location and it would allow to continue to show the content of another bank. With the scanline counter and proper timed code, it wouldn't be an issue. This is how you would display a pictures that exceed the amount of 256 tiles per pattern table.

To be honnest, if your goal is to have 512 tiles for background instead of the usual 256, and that you're going to do that with a mapper, I'd rather suggest using MMC2 or MMC4 than MMC3. Those mappers were developped exactly for this purpose.

Author:  Dwedit [ Thu Nov 08, 2018 10:32 am ]
Post subject:  Re: Trying to display all tiles of CHR on screen

There was a video of someone who used a separate processor to display arbitrary images using the NES PPU, but still had noise in his final image.

https://youtu.be/ar9WRwCiSr0
https://www.youtube.com/watch?v=hTlNVUmBA28

Author:  rainwarrior [ Thu Nov 08, 2018 11:50 am ]
Post subject:  Re: Trying to display all tiles of CHR on screen

tom7's noise isn't an intractable problem though, rasteri had done the same thing a lot more cleanly prior:
http://forums.nesdev.com/viewtopic.php?f=22&t=16807

(There is another form of noise for the dithering, but that was intentionally added. You can verify that it's a stable picture wherever the screen is not moving.)

Bregalad wrote:
To be honnest, if your goal is to have 512 tiles for background instead of the usual 256, and that you're going to do that with a mapper, I'd rather suggest using MMC2 or MMC4 than MMC3. Those mappers were developped exactly for this purpose.

I don't think there's a need to have any mapper at all for this intended purpose, plain NROM is good enough for this if 8k of CHR is sufficient.

Just wait for vblank, set page 0, use a countdown loop to wait for half the screen to pass, then set page 1. (Can probably even do this in C without much problem, i.e. inside that while(1) loop.)

Author:  Haruka [ Thu Nov 08, 2018 5:48 pm ]
Post subject:  Re: Trying to display all tiles of CHR on screen

Bregalad wrote:
To be honnest, if your goal is to have 512 tiles for background instead of the usual 256, and that you're going to do that with a mapper, I'd rather suggest using MMC2 or MMC4 than MMC3. Those mappers were developped exactly for this purpose.

MMC3 is much more common than MMC2/MMC4. Easier to find and cheaper to buy.

Author:  Haruka [ Thu Nov 08, 2018 5:58 pm ]
Post subject:  Re: Trying to display all tiles of CHR on screen

Using sprite 0 hit check perfectly solved my problem!

Attachment:
0000.png
0000.png [ 18.5 KiB | Viewed 3037 times ]

Attachment:
0001.png
0001.png [ 18.84 KiB | Viewed 3037 times ]


Here is the code:
Code:
#define PPUCTRL   (*(unsigned char*)0x2000)
#define PPUMASK   (*(unsigned char*)0x2001)
#define PPUSTAT   (*(unsigned char*)0x2002)
#define OAMADDR   (*(unsigned char*)0x2003)
#define OAMDATA   (*(unsigned char*)0x2004)
#define PPUADDR   (*(unsigned char*)0x2006)
#define PPUDATA   (*(unsigned char*)0x2007)


static const unsigned char bg_palette[16] = {0x0F, 0x00, 0x10, 0x20,   0x01, 0x11, 0x21, 0x31,      0x05, 0x15, 0x25, 0x35,      0x0A, 0x1A, 0x2A, 0x3A};
static const unsigned char spr_palette[16] = {0x0F, 0x13, 0x23, 0x33,   0x01, 0x71, 0x27, 0x37,      0x05, 0x1C, 0x2C, 0x3C,      0x0A, 0x19, 0x29, 0x39};


void isr_NMI(void);


static void waitForVblank(void){
   while((PPUSTAT & 0x80) != 0);
}

static void fillPalette(void){
   unsigned char i = 0;
   // change palette during vblank
   waitForVblank();
   // set palette
   PPUADDR = (unsigned char)0x3F;
   PPUADDR = (unsigned char)0x00;
   for (i = 0; i < 16; ++i){
      PPUDATA = *(bg_palette + i);
   }
   for (i = 0; i < 16; ++i){
      PPUDATA = *(spr_palette + i);
   }
}

static void drawScreen_C(void){
   unsigned char x = 0, y = 0;
   unsigned char idx = 0;
   unsigned short t = 0;
   idx = 0x40;
   for(y=6;y<15;++y){
      unsigned short addr = (unsigned short)(0x2000 + y * 32 + 6);
      PPUADDR = (unsigned char)((addr >> 8) & 0xFF);
      PPUADDR = (unsigned char)((addr >> 0) & 0xFF);
      for(x=6;x<26;++x){
         PPUDATA = idx++;
      }
      waitForVblank();
   }
   idx = 0x40;
   for(y=15;y<24;++y){
      unsigned short addr = (unsigned short)(0x2000 + y * 32 + 6);
      PPUADDR = (unsigned char)((addr >> 8) & 0xFF);
      PPUADDR = (unsigned char)((addr >> 0) & 0xFF);
      for(x=6;x<26;++x){
         PPUDATA = idx++;
      }
      waitForVblank();
   }
   // fill the left-most column of the screen with non-transparent color for sprite 0 hit check
   for(y=0;y<30;++y){
      unsigned short addr = (unsigned short)(0x2000 + y * 32);
      PPUADDR = (unsigned char)((addr >> 8) & 0xFF);
      PPUADDR = (unsigned char)((addr >> 0) & 0xFF);
      PPUDATA = 1;
   }
}

static void addSprite0(void){
   waitForVblank();
   OAMADDR = 0x00;
   OAMDATA = 119;
   OAMDATA = 1;
   OAMDATA = 0;
   OAMDATA = 0;
}

void isr_NMI(){
   //drawScreen_C();
}

void main(void){
   // enable vblank
   PPUCTRL = (unsigned char)0x80;

   fillPalette();

   drawScreen_C();
   
   addSprite0();
   
   // turn on rendering
   PPUMASK = (unsigned char)0x1E;

   while(1){
      // change pattern table page when sprite 0 hit happened, and back when vblank finished
      if((PPUSTAT & 0x40) != 0){
         PPUCTRL = (unsigned char)0x90;
      }else{
         PPUCTRL = (unsigned char)0x80;
      }
   }
}

Author:  Dwedit [ Thu Nov 08, 2018 10:32 pm ]
Post subject:  Re: [SOLVED]Trying to display all tiles of CHR on screen

Note that sprite 0 hits require....a sprite 0 hit. At least one nontransparent pixel from the sprite needs to overlap at least one nontransparent pixel from the background tile.

Page 1 of 2 All times are UTC - 7 hours
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/