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):
And now for the VRAM/VROM read hook:
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
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;
// ...
}
}
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);
}
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