I have no idea what is wrong with my MMC5 CHR swap :-/

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
I have no idea what is wrong with my MMC5 CHR swap :-/
by on (#106777)
OK, I am trying to get MMC5 implemented correctly and I honestly don't see where I am going wrong.

Here's the quirk rundown. I have my mapper's able to hook VRAM reads/writes so they can do what they like there. Here's the relevant parts (snipped for brevity):

Code:
void Mapper5::write_5(uint16_t address, uint8_t value) {

   switch(address) {
   // ...
   case 0x5101:
      chr_mode_ = value & 0x03;
      break;
   case 0x5120:
   case 0x5121:
   case 0x5122:
   case 0x5123:
   case 0x5124:
   case 0x5125:
   case 0x5126:
   case 0x5127:
      sp_chr_banks_[address & 0x07] = value;
      last_chr_write_ = CHR_BANK_A;
      break;
   case 0x5128:
   case 0x5129:
   case 0x512a:
   case 0x512b:
      bg_chr_banks_[(address & 0x07) ^ 0x00] = value;
      bg_chr_banks_[(address & 0x07) ^ 0x04] = value; // mirrored here so the implementation later is simpler
      last_chr_write_ = CHR_BANK_B;
      break;
   // ...
   }
}


And now for the VRAM/VROM read hook:

Code:
uint8_t Mapper5::read_vram(uint16_t address) {

   switch((address >> 10) & 0x0f) {
   // ...
   case 0x00:
   case 0x01:
   case 0x02:
   case 0x03:
   case 0x04:
   case 0x05:
   case 0x06:
   case 0x07:
      // CHR-ROM ($0000 - $1fff)
      const uint8_t *chr_selector;
      
      if(sprite_size_ == 16) {
         // sprite fetches seem to bebe between fetch #130 and #162
         // I've tried basing this on a few factors, more or less same results
         if(fetch_count_ >= 130 && fetch_count_ < 162) {
            chr_selector = sp_chr_banks_;
         } else {
            chr_selector = bg_chr_banks_;
         }
      } else {
         if(last_chr_write_ == CHR_BANK_A) {
            chr_selector = sp_chr_banks_;
         } else {
            chr_selector = bg_chr_banks_;
         }
      }
      
      const uint8_t *chr_rom_banks[8];
      const uint8_t *const chr_rom  = nes::cart.chr();
      const uint32_t chr_mask       = nes::cart.chr_mask();
      
      switch(chr_mode_ & 0x03) {
      case 0x00: // 8K mode
         chr_rom_banks[0] = chr_rom + ((chr_selector[7] * 0x2000) & chr_mask) + 0x0000; // $0000
         chr_rom_banks[1] = chr_rom + ((chr_selector[7] * 0x2000) & chr_mask) + 0x0400; // $0400
         chr_rom_banks[2] = chr_rom + ((chr_selector[7] * 0x2000) & chr_mask) + 0x0800; // $0800
         chr_rom_banks[3] = chr_rom + ((chr_selector[7] * 0x2000) & chr_mask) + 0x0c00; // $0c00
         chr_rom_banks[4] = chr_rom + ((chr_selector[7] * 0x2000) & chr_mask) + 0x1000; // $1000
         chr_rom_banks[5] = chr_rom + ((chr_selector[7] * 0x2000) & chr_mask) + 0x1400; // $1400
         chr_rom_banks[6] = chr_rom + ((chr_selector[7] * 0x2000) & chr_mask) + 0x1800; // $1800
         chr_rom_banks[7] = chr_rom + ((chr_selector[7] * 0x2000) & chr_mask) + 0x1c00; // $1c00
         break;
      case 0x01: // 4K mode
         chr_rom_banks[0] = chr_rom + ((chr_selector[3] * 0x1000) & chr_mask) + 0x0000; // $0000
         chr_rom_banks[1] = chr_rom + ((chr_selector[3] * 0x1000) & chr_mask) + 0x0400; // $0400
         chr_rom_banks[2] = chr_rom + ((chr_selector[3] * 0x1000) & chr_mask) + 0x0800; // $0800
         chr_rom_banks[3] = chr_rom + ((chr_selector[3] * 0x1000) & chr_mask) + 0x0c00; // $0c00
         chr_rom_banks[4] = chr_rom + ((chr_selector[7] * 0x1000) & chr_mask) + 0x0000; // $1000
         chr_rom_banks[5] = chr_rom + ((chr_selector[7] * 0x1000) & chr_mask) + 0x0400; // $1400
         chr_rom_banks[6] = chr_rom + ((chr_selector[7] * 0x1000) & chr_mask) + 0x0800; // $1800
         chr_rom_banks[7] = chr_rom + ((chr_selector[7] * 0x1000) & chr_mask) + 0x0c00; // $1c00
         break;
      case 0x02: // 2K mode
         chr_rom_banks[0] = chr_rom + ((chr_selector[1] * 0x0800) & chr_mask) + 0x0000; // $0000
         chr_rom_banks[1] = chr_rom + ((chr_selector[1] * 0x0800) & chr_mask) + 0x0400; // $0400
         chr_rom_banks[2] = chr_rom + ((chr_selector[3] * 0x0800) & chr_mask) + 0x0000; // $0800
         chr_rom_banks[3] = chr_rom + ((chr_selector[3] * 0x0800) & chr_mask) + 0x0400; // $0c00
         chr_rom_banks[4] = chr_rom + ((chr_selector[5] * 0x0800) & chr_mask) + 0x0000; // $1000
         chr_rom_banks[5] = chr_rom + ((chr_selector[5] * 0x0800) & chr_mask) + 0x0400; // $1400
         chr_rom_banks[6] = chr_rom + ((chr_selector[7] * 0x0800) & chr_mask) + 0x0000; // $1800
         chr_rom_banks[7] = chr_rom + ((chr_selector[7] * 0x0800) & chr_mask) + 0x0400; // $1c00
         break;
      case 0x03: // 1K mode
         chr_rom_banks[0] = chr_rom + ((chr_selector[0] * 0x0400) & chr_mask) + 0x0000; // $0000
         chr_rom_banks[1] = chr_rom + ((chr_selector[1] * 0x0400) & chr_mask) + 0x0000; // $0400
         chr_rom_banks[2] = chr_rom + ((chr_selector[2] * 0x0400) & chr_mask) + 0x0000; // $0800
         chr_rom_banks[3] = chr_rom + ((chr_selector[3] * 0x0400) & chr_mask) + 0x0000; // $0c00
         chr_rom_banks[4] = chr_rom + ((chr_selector[4] * 0x0400) & chr_mask) + 0x0000; // $1000
         chr_rom_banks[5] = chr_rom + ((chr_selector[5] * 0x0400) & chr_mask) + 0x0000; // $1400
         chr_rom_banks[6] = chr_rom + ((chr_selector[6] * 0x0400) & chr_mask) + 0x0000; // $1800
         chr_rom_banks[7] = chr_rom + ((chr_selector[7] * 0x0400) & chr_mask) + 0x0000; // $1c00
         break;
      }

      return chr_rom_banks[(address >> 10) & 0x0f][0x03ff]; // for reads at $0000-$1ffff result is our implementation
   }
   
   // default implementation if not caught above
   return Mapper::read_vram(address);
}


The current result is a mostly white title screen and single color blocks during game play (attributes seem right...).

I am currently counting fetches to determine when rendering sprites/bg. My IRQ is implemented in terms of 3 consecutive identical reads (and seems to fire at the correct point). When the IRQ fires, I reset the "fetch count" to 0. So it should be 0-130 for BG rendering, 130-162 for sprites, and 162+ for next scanline BG.

I have been staring at this for a while now and can't find my mistake.

hopefully it's something obvious someone here can point out.


NOTE: I've chosen for now to implement CHR set "B" like the "proposed revision" in Disch's Doc. (where $512B is a full 8K in 8K mode).

Thanks,
proxy
Re: I have no idea what is wrong with my MMC5 CHR swap :-/
by on (#106839)
OK, I missed something **really** obvious and feel kinda dumb missing it :-P.

Code:
return chr_rom_banks[(address >> 10) & 0x0f][0x03ff];


should **obviously** be:

Code:
return chr_rom_banks[(address >> 10) & 0x0f][address & 0x03ff];


The graphics look pretty much perfect now.