CHR loading code (C++) don't quite understand CHR format...

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
CHR loading code (C++) don't quite understand CHR format...
by on (#185781)
What I (think I) understand about the basic CHR format, not counting setting colors, for the standard 2BPP NES format, from hex viewing:
Each byte acts like a vertical bitflag, e.g. 0 = nothing there, 1 = pixel on 1st row, 2 = pixel on 2nd row, 3 = pixel on 1st and 2nd row etc...

And it is repeated for each color every 8(???) bytes. Something like that. But I'm NOT interested in color right now, I just want the shape to be right!

I am attempting to load CHR with this code:
Using the default rotation: 270.

Code:
unsigned char _bit[9];
   _bit[0] = 0x00;
   _bit[1] = 0x01;
   _bit[2] = 0x02;
   _bit[3] = 0x04;
   _bit[4] = 0x08;
   _bit[5] = 0x10;
   _bit[6] = 0x20;
   _bit[7] = 0x40;
   _bit[8] = 0x80;

int _i = 0;

// ...

while (chr.read(&_b, 1)) {
   if (_i <= imgw) {

      std::cout << "#" << _i << ";\n";

      bool bPixel = false;

      for (int _ii=1; _ii<8; ++_ii) {
         bool bPixel = (_bit[_ii] &_b != 0);
         if (bPixel) {
            tile[_ii-1][_i][_b] = 255; // TESTING PURPOSES NO COLOR YET
         }
      }

      std::cout << "\n";

      _i = _i + 1;
   }
}


Works great. For the first row.
Otherwise I just get garbage that is very vaguely reminiscent of a messed up version of the tile.

Can someone clear up a few things on the CHR format? How should I really be loading this? And yes, I know my code is probably horribly written...

I really hope this is the right place to post it. CHR info is kind of scarce.
Re: CHR loading code (C++) don't quite understand CHR format
by on (#185782)
There's a page on the wiki that describes it pretty well, I think.

https://wiki.nesdev.com/w/index.php/PPU_pattern_tables

Each byte represents a row, each bit represents a column. Each tile is 16 bytes.

If a color for a pixel is %XY (two bits)
Bytes 0-7 are the low bit of the color (The Y bit above) for each row/column.
Bytes 8-15 are the high bit of the color (The X bit above).

If you have byte 0 as this: 0 1 0 0 1 1 1 1
If you have byte 8 as this: 0 0 0 1 0 1 0 1

The first row of the tile is: 00 01 00 10 01 11 01 11
or color 0, 1, 0, 2, 1, 3, 1, 3

Then you read byte 1 and and byte 9 to get the second row.
at byte 16, it's a new tile.
Edit:
My code looks a bit like this:
Code:
int decode_chr(int byte1, int byte2, int andvalue){//andvalue is the bitmask
   if(byte1&andvalue){
      if(byte2&andvalue){
         return 3;
      }else{
         return 1;
      }
   }else{
      if(byte2&andvalue){
         return 2;
      }else{
         return 0;
      }
   }
}//Verfied

      for(int x = 0; x < 16; x++){
         for(int y = 0; y < 16; y++){
            bufferpos = x * 16+y*256;
            for(int row = 0; row < 8; row++){
               chrarray[x*8+0][y*8+row] = decode_chr(buffer[bufferpos], buffer[bufferpos+8], 0x80);
               chrarray[x*8+1][y*8+row] = decode_chr(buffer[bufferpos], buffer[bufferpos+8], 0x40);
               chrarray[x*8+2][y*8+row] = decode_chr(buffer[bufferpos], buffer[bufferpos+8], 0x20);
               chrarray[x*8+3][y*8+row] = decode_chr(buffer[bufferpos], buffer[bufferpos+8], 0x10);
               chrarray[x*8+4][y*8+row] = decode_chr(buffer[bufferpos], buffer[bufferpos+8], 0x08);
               chrarray[x*8+5][y*8+row] = decode_chr(buffer[bufferpos], buffer[bufferpos+8], 0x04);
               chrarray[x*8+6][y*8+row] = decode_chr(buffer[bufferpos], buffer[bufferpos+8], 0x02);
               chrarray[x*8+7][y*8+row] = decode_chr(buffer[bufferpos], buffer[bufferpos+8], 0x01);

               bufferpos++;
            }
            
         }
      }


It assumes the CHR is 256 tiles arranged in a grid of 16x16 tiles. Each tile is 16 bytes, so x (column) is multiplied by 16, y is multiplied by 256 (16 columns skips a row of tiles) to set bufferpos, which is the first byte of the tile at the X, Y position. Then it feeds the byte at that location, and the byte 8 bytes ahead to decode chr to get the color, which is placed in an array.

Edit2: I would name your variables things that aren't i and such.
Anyway, this isn't a clever way to do it, and I know it could be condensed. But it does work.

Edit3: Fixed a small error in the explanation.
Re: CHR loading code (C++) don't quite understand CHR format
by on (#185785)
What isn't immediately apparent (because it's hard to depict in ASCII -- but the very top of the wiki page linked there DOES explain it) is that the two "bit planes" are essentially "overlayed" (for lack of better term -- I like to think of them as OR'd when just referring to raw black and white (pixel on, pixel off)) which is what ends up making the colour range from 0-4 (2 bits per pixel, one from each "plane").

The numbers in the "Pixel Pattern" depict what colour to use, ranging from 0 to 3 (again: 2 bits per pixel).

The attribute table (not going to discuss that in depth right now, but just noting this for clarity) is what holds the "remaining" 2 bits, thus making the pixel colour essentially 4 bits, hence values 0 to 15.

Trust me: once you see it/get it, it'll suddenly click and make a whole ton of sense and you'll be thrilled.
Re: CHR loading code (C++) don't quite understand CHR format
by on (#185788)
ittyBittyByte wrote:
Each byte acts like a vertical bitflag, e.g. 0 = nothing there, 1 = pixel on 1st row, 2 = pixel on 2nd row, 3 = pixel on 1st and 2nd row etc...

And it is repeated for each color every 8(???) bytes.

Where did you get this information from? That doesn't sound anything like NES patterns. Maybe you were reading about the compression format used by an NES game and mistook it for the format the PPU itself uses? If that's the case, you don't have to worry about game-specific compression formats, because the games themselves will take care of decompressing them to the format the PPU expects.

Quote:
CHR info is kind of scarce.

You probably won't find much information about it because the format is pretty straightforward and well documented. There's just not much to say about it.
Re: CHR loading code (C++) don't quite understand CHR format
by on (#185793)
I got that information by using a hex editor, messing in YY-CHR, and saving over and over. Because on a blank CHR when I set pixel 0,0 to color 1 the first byte became 01, when I set pixel 0,1 to color 1 the first byte became 2, and when I set 0,0 and 0,1 to color 1 it became 3. Then I figured out that setting only 0,8 to color 1 made the first byte the hex equivelant 127 (or maybe it was 128, dont remember). And setting all pixels from 0,0 to 0,8 in a column of pixels to color 1 gave me FF for the first byte. So I assumed it worked like some sort of bit flag thing. I knew it was probably wrong though...
Re: CHR loading code (C++) don't quite understand CHR format
by on (#185797)
This is part of a program I wrote which can do this. The value of "parameter" needs to be 8 for use with NES/Famicom.
Code:
static int initpixel_F2(void) {
  if(!parameter) parameter=1;
  buf=calloc(2,curbyte=parameter);
  return !buf;
}

static int getpixel_F2(void) {
  int i;
  if(curbyte==parameter) {
    curbyte=rowpos=0;
    fread(buf,2,parameter,stdin);
  }
  i=buf[curbyte]&128?1:0;
  i|=buf[parameter+curbyte]&128?2:0;
  buf[curbyte]<<=1;
  buf[parameter+curbyte]<<=1;
  if(++rowpos==8) {
    rowpos=0;
    curbyte++;
  }
  return i;
}
Re: CHR loading code (C++) don't quite understand CHR format
by on (#185923)
Thanks guys, I got it to work by messing with Kasumi's code for 7 hours... :P