It is currently Tue Nov 13, 2018 9:22 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Wed Nov 07, 2018 5:55 pm 
Offline

Joined: Fri Mar 23, 2018 8:58 pm
Posts: 16
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 399 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 394 times ]


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


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


Last edited by Haruka on Thu Nov 08, 2018 6:00 pm, edited 1 time in total.

Top
 Profile  
 
PostPosted: Wed Nov 07, 2018 6:23 pm 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2035
Location: Fukuoka, Japan
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.


Top
 Profile  
 
PostPosted: Wed Nov 07, 2018 7:39 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
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.


Top
 Profile  
 
PostPosted: Wed Nov 07, 2018 8:58 pm 
Offline

Joined: Fri Mar 23, 2018 8:58 pm
Posts: 16
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.


Top
 Profile  
 
PostPosted: Wed Nov 07, 2018 9:11 pm 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2035
Location: Fukuoka, Japan
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.


Top
 Profile  
 
PostPosted: Wed Nov 07, 2018 9:34 pm 
Offline

Joined: Fri Mar 23, 2018 8:58 pm
Posts: 16
MMC3 is a good idea. But currently I don't know how to manipulate MMC3. Maybe I could try searching some sample code.


Top
 Profile  
 
PostPosted: Wed Nov 07, 2018 9:36 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
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.


Top
 Profile  
 
PostPosted: Wed Nov 07, 2018 10:41 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 10959
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Thu Nov 08, 2018 1:21 am 
Offline

Joined: Fri Mar 23, 2018 8:58 pm
Posts: 16
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.


Top
 Profile  
 
PostPosted: Thu Nov 08, 2018 9:27 am 
Offline
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7566
Location: Chexbres, VD, Switzerland
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.


Top
 Profile  
 
PostPosted: Thu Nov 08, 2018 10:32 am 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 4104
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

_________________
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!


Top
 Profile  
 
PostPosted: Thu Nov 08, 2018 11:50 am 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 6948
Location: Canada
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.)


Top
 Profile  
 
PostPosted: Thu Nov 08, 2018 5:48 pm 
Offline

Joined: Fri Mar 23, 2018 8:58 pm
Posts: 16
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.


Top
 Profile  
 
PostPosted: Thu Nov 08, 2018 5:58 pm 
Offline

Joined: Fri Mar 23, 2018 8:58 pm
Posts: 16
Using sprite 0 hit check perfectly solved my problem!

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

Attachment:
0001.png
0001.png [ 18.84 KiB | Viewed 181 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;
      }
   }
}


Top
 Profile  
 
PostPosted: Thu Nov 08, 2018 10:32 pm 
Offline
User avatar

Joined: Fri Nov 19, 2004 7:35 pm
Posts: 4104
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.

_________________
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 17 posts ]  Go to page 1, 2  Next

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group