nes: Improve MMC5 extended attributes

- mmc5: Use ExRAM byte from nametable area to select palette bank
- ppu2c0x: Fix order of nametable/attribute reads
This commit is contained in:
AJR 2019-04-12 08:39:16 -04:00
parent 106990d8c9
commit cb514ba0f1
4 changed files with 253 additions and 243 deletions

View File

@ -33620,7 +33620,7 @@
</part> </part>
</software> </software>
<software name="simcity" supported="partial"> <software name="simcity">
<description>SimCity (USA, Prototype)</description> <description>SimCity (USA, Prototype)</description>
<year>1991</year> <year>1991</year>
<publisher>Nintendo</publisher> <publisher>Nintendo</publisher>

View File

@ -45,7 +45,7 @@ nes_exrom_device::nes_exrom_device(const machine_config &mconfig, const char *ta
: nes_nrom_device(mconfig, NES_EXROM, tag, owner, clock), m_irq_count(0) : nes_nrom_device(mconfig, NES_EXROM, tag, owner, clock), m_irq_count(0)
, m_irq_status(0), m_irq_enable(0), m_mult1(0), m_mult2(0), m_mmc5_scanline(0), m_vrom_page_a(0), m_vrom_page_b(0), m_floodtile(0), m_floodattr(0) , m_irq_status(0), m_irq_enable(0), m_mult1(0), m_mult2(0), m_mmc5_scanline(0), m_vrom_page_a(0), m_vrom_page_b(0), m_floodtile(0), m_floodattr(0)
, m_prg_mode(0), m_chr_mode(0), m_wram_protect_1(0), m_wram_protect_2(0), m_exram_control(0), m_wram_base(0), m_last_chr(0), m_ex1_chr(0) , m_prg_mode(0), m_chr_mode(0), m_wram_protect_1(0), m_wram_protect_2(0), m_exram_control(0), m_wram_base(0), m_last_chr(0), m_ex1_chr(0)
, m_split_chr(0), m_ex1_bank(0), m_high_chr(0), m_split_scr(0), m_split_rev(0), m_split_ctrl(0), m_split_yst(0), m_split_bank(0), m_vcount(0) , m_split_chr(0), m_ex1_bank(0), m_ex1_attrib(0), m_high_chr(0), m_split_scr(0), m_split_rev(0), m_split_ctrl(0), m_split_yst(0), m_split_bank(0), m_vcount(0)
, m_ppu(*this, ":ppu") // FIXME: this dependency should not exist , m_ppu(*this, ":ppu") // FIXME: this dependency should not exist
, m_sound(*this, "mmc5snd") // FIXME: this is a hack, it should be separated device, similar not same as NES APU!!! , m_sound(*this, "mmc5snd") // FIXME: this is a hack, it should be separated device, similar not same as NES APU!!!
{ {
@ -81,6 +81,7 @@ void nes_exrom_device::device_start()
save_item(NAME(m_prg_regs)); save_item(NAME(m_prg_regs));
save_item(NAME(m_prg_ram_mapped)); save_item(NAME(m_prg_ram_mapped));
save_item(NAME(m_ex1_bank)); save_item(NAME(m_ex1_bank));
save_item(NAME(m_ex1_attrib));
save_item(NAME(m_high_chr)); save_item(NAME(m_high_chr));
save_item(NAME(m_split_scr)); save_item(NAME(m_split_scr));
save_item(NAME(m_split_rev)); save_item(NAME(m_split_rev));
@ -124,6 +125,7 @@ void nes_exrom_device::pcb_reset()
m_ex1_chr = 0; m_ex1_chr = 0;
m_split_chr = 0; m_split_chr = 0;
m_ex1_bank = 0; m_ex1_bank = 0;
m_ex1_attrib = 0;
m_vcount = 0; m_vcount = 0;
for (auto & elem : m_vrom_bank) for (auto & elem : m_vrom_bank)
@ -167,70 +169,70 @@ void nes_exrom_device::update_prg()
switch (m_prg_mode) switch (m_prg_mode)
{ {
case 0: // 32k banks case 0: // 32k banks
bank3 = m_prg_regs[3] >> 2; bank3 = m_prg_regs[3] >> 2;
prg32(bank3); prg32(bank3);
break; break;
case 1: // 16k banks case 1: // 16k banks
bank1 = m_prg_regs[1] >> 1; bank1 = m_prg_regs[1] >> 1;
bank3 = m_prg_regs[3] >> 1; bank3 = m_prg_regs[3] >> 1;
if (m_prg_ram_mapped[1]) if (m_prg_ram_mapped[1])
{ {
m_ram_hi_banks[0] = ((bank1 << 1) & 0x07); m_ram_hi_banks[0] = ((bank1 << 1) & 0x07);
m_ram_hi_banks[1] = ((bank1 << 1) & 0x07) | 1; m_ram_hi_banks[1] = ((bank1 << 1) & 0x07) | 1;
} }
else else
prg16_89ab(bank1); prg16_89ab(bank1);
prg16_cdef(bank3); prg16_cdef(bank3);
break; break;
case 2: // 16k-8k banks case 2: // 16k-8k banks
bank1 = m_prg_regs[1] >> 1; bank1 = m_prg_regs[1] >> 1;
bank2 = m_prg_regs[2]; bank2 = m_prg_regs[2];
bank3 = m_prg_regs[3]; bank3 = m_prg_regs[3];
if (m_prg_ram_mapped[1]) if (m_prg_ram_mapped[1])
{ {
m_ram_hi_banks[0] = ((bank1 << 1) & 0x07); m_ram_hi_banks[0] = ((bank1 << 1) & 0x07);
m_ram_hi_banks[1] = ((bank1 << 1) & 0x07) | 1; m_ram_hi_banks[1] = ((bank1 << 1) & 0x07) | 1;
} }
else else
prg16_89ab(bank1); prg16_89ab(bank1);
if (m_prg_ram_mapped[2]) if (m_prg_ram_mapped[2])
m_ram_hi_banks[2] = (bank2 & 0x07); m_ram_hi_banks[2] = (bank2 & 0x07);
else else
prg8_cd(bank2); prg8_cd(bank2);
prg8_ef(bank3); prg8_ef(bank3);
break; break;
case 3: // 8k banks case 3: // 8k banks
bank0 = m_prg_regs[0]; bank0 = m_prg_regs[0];
bank1 = m_prg_regs[1]; bank1 = m_prg_regs[1];
bank2 = m_prg_regs[2]; bank2 = m_prg_regs[2];
bank3 = m_prg_regs[3]; bank3 = m_prg_regs[3];
if (m_prg_ram_mapped[0]) if (m_prg_ram_mapped[0])
m_ram_hi_banks[0] = (bank0 & 0x07); m_ram_hi_banks[0] = (bank0 & 0x07);
else else
prg8_89(bank0); prg8_89(bank0);
if (m_prg_ram_mapped[1]) if (m_prg_ram_mapped[1])
m_ram_hi_banks[1] = (bank1 & 0x07); m_ram_hi_banks[1] = (bank1 & 0x07);
else else
prg8_ab(bank1); prg8_ab(bank1);
if (m_prg_ram_mapped[2]) if (m_prg_ram_mapped[2])
m_ram_hi_banks[2] = (bank2 & 0x07); m_ram_hi_banks[2] = (bank2 & 0x07);
else else
prg8_cd(bank2); prg8_cd(bank2);
prg8_ef(bank3); prg8_ef(bank3);
break; break;
} }
} }
@ -258,20 +260,20 @@ void nes_exrom_device::set_mirror(int page, int src)
{ {
switch (src) switch (src)
{ {
case 0: case 0:
set_nt_page(page, CIRAM, 0, 1); set_nt_page(page, CIRAM, 0, 1);
break; break;
case 1: case 1:
set_nt_page(page, CIRAM, 1, 1); set_nt_page(page, CIRAM, 1, 1);
break; break;
case 2: case 2:
set_nt_page(page, EXRAM, 0, 1); set_nt_page(page, EXRAM, 0, 1);
break; break;
case 3: case 3:
set_nt_page(page, MMC5FILL, 0, 0); set_nt_page(page, MMC5FILL, 0, 0);
break; break;
default: default:
fatalerror("This should never happen\n"); fatalerror("This should never happen\n");
} }
} }
@ -295,49 +297,50 @@ uint8_t nes_exrom_device::nt_r(offs_t offset)
switch (m_nt_src[page]) switch (m_nt_src[page])
{ {
case MMC5FILL: case MMC5FILL:
if ((offset & 0x3ff) >= 0x3c0)
return m_floodattr;
return m_floodtile;
case EXRAM:
// to investigate: can split screen affect this too?
if (!BIT(m_exram_control, 1))
return m_exram[offset & 0x3ff];
else
return 0x00;
case CIRAM:
default:
// Uchuu Keibitai SDF uses extensively split screen for its intro,
// but it does not work yet
if (m_split_scr && !(m_exram_control & 0x02) && in_split())
{
int tile = m_ppu->get_tilenum();
if ((offset & 0x3ff) >= 0x3c0) if ((offset & 0x3ff) >= 0x3c0)
return m_floodattr; {
return m_floodtile; int pos = (((m_split_yst + m_vcount) & ~0x1f) | (tile & 0x1f)) >> 2;
return m_exram[0x3c0 | pos];
case EXRAM: }
// to investigate: can split screen affect this too?
if (!BIT(m_exram_control, 1))
return m_exram[offset & 0x3ff];
else else
return 0x00;
case CIRAM:
default:
// Uchuu Keibitai SDF uses extensively split screen for its intro,
// but it does not work yet
if (m_split_scr && !(m_exram_control & 0x02) && in_split())
{ {
int tile = m_ppu->get_tilenum(); int pos = (((m_split_yst + m_vcount) & 0xf8) << 2) | (tile & 0x1f);
return m_exram[pos];
if ((offset & 0x3ff) >= 0x3c0)
{
int pos = (((m_split_yst + m_vcount) & ~0x1f) | (tile & 0x1f)) >> 2;
return m_exram[0x3c0 | pos];
}
else
{
int pos = (((m_split_yst + m_vcount) & 0xf8) << 2) | (tile & 0x1f);
return m_exram[pos];
}
} }
}
if (m_exram_control == 1) if (m_exram_control == 1)
{
if ((offset & 0x3ff) >= 0x3c0)
return m_ex1_attrib;
else if (!machine().side_effects_disabled()) // in this case, we write Ex1 CHR bank, but then access NT normally!
{ {
if ((offset & 0x3ff) >= 0x3c0) m_ex1_chr = 1;
return m_mmc5_attrib[(m_exram[offset & 0x3ff] >> 6) & 0x03]; m_ex1_bank = (m_exram[offset & 0x3ff] & 0x3f) | (m_high_chr << 6);
else // in this case, we write Ex1 CHR bank, but then access NT normally! m_ex1_attrib = m_mmc5_attrib[(m_exram[offset & 0x3ff] >> 6) & 0x03];
{
m_ex1_chr = 1;
m_ex1_bank = (m_exram[offset & 0x3ff] & 0x3f) | (m_high_chr << 6);
}
} }
return m_nt_access[page][offset & 0x3ff]; }
return m_nt_access[page][offset & 0x3ff];
} }
} }
@ -350,14 +353,14 @@ void nes_exrom_device::nt_w(offs_t offset, uint8_t data)
switch (m_nt_src[page]) switch (m_nt_src[page])
{ {
case EXRAM: case EXRAM:
m_exram[offset & 0x3ff] = data; m_exram[offset & 0x3ff] = data;
break; break;
case CIRAM: case CIRAM:
default: default:
m_nt_access[page][offset & 0x3ff] = data; m_nt_access[page][offset & 0x3ff] = data;
break; break;
} }
} }
@ -367,21 +370,21 @@ inline uint8_t nes_exrom_device::base_chr_r(int bank, uint32_t offset)
switch (m_chr_mode) switch (m_chr_mode)
{ {
case 0: case 0:
if (bank < 8) if (bank < 8)
helper = ((m_vrom_bank[bank | 7] & 0xff) * 0x2000) + (offset & 0x1fff); helper = ((m_vrom_bank[bank | 7] & 0xff) * 0x2000) + (offset & 0x1fff);
else else
helper = ((m_vrom_bank[bank | 3] & 0xff) * 0x2000) + (offset & 0xfff); helper = ((m_vrom_bank[bank | 3] & 0xff) * 0x2000) + (offset & 0x1fff);
break; break;
case 1: case 1:
helper = ((m_vrom_bank[bank | 3] & 0xff) * 0x1000) + (offset & 0xfff); helper = ((m_vrom_bank[bank | 3] & 0xff) * 0x1000) + (offset & 0xfff);
break; break;
case 2: case 2:
helper = (m_vrom_bank[bank | 1] * 0x800) + (offset & 0x7ff); helper = (m_vrom_bank[bank | 1] * 0x800) + (offset & 0x7ff);
break; break;
case 3: case 3:
helper = (m_vrom_bank[bank] * 0x400) + (offset & 0x3ff); helper = (m_vrom_bank[bank] * 0x400) + (offset & 0x3ff);
break; break;
} }
return m_vrom[helper & (m_vrom_size - 1)]; return m_vrom[helper & (m_vrom_size - 1)];
@ -444,20 +447,25 @@ uint8_t nes_exrom_device::read_l(offs_t offset)
switch (offset) switch (offset)
{ {
case 0x1204: case 0x1204:
value = m_irq_status; value = m_irq_status;
if (!machine().side_effects_disabled())
{
m_irq_status &= ~0x80; m_irq_status &= ~0x80;
set_irq_line(CLEAR_LINE); set_irq_line(CLEAR_LINE);
return value; }
return value;
case 0x1205: case 0x1205:
return (m_mult1 * m_mult2) & 0xff; return (m_mult1 * m_mult2) & 0xff;
case 0x1206:
return ((m_mult1 * m_mult2) & 0xff00) >> 8;
default: case 0x1206:
return ((m_mult1 * m_mult2) & 0xff00) >> 8;
default:
if (!machine().side_effects_disabled())
logerror("MMC5 uncaught read, offset: %04x\n", offset + 0x4100); logerror("MMC5 uncaught read, offset: %04x\n", offset + 0x4100);
return get_open_bus(); return get_open_bus();
} }
} }
@ -491,128 +499,130 @@ void nes_exrom_device::write_l(offs_t offset, uint8_t data)
switch (offset) switch (offset)
{ {
case 0x1100: case 0x1100:
m_prg_mode = data & 0x03; m_prg_mode = data & 0x03;
update_prg(); update_prg();
//LOG_MMC(("MMC5 rom bank mode: %02x\n", data)); //LOG_MMC(("MMC5 rom bank mode: %02x\n", data));
break; break;
case 0x1101: case 0x1101:
m_chr_mode = data & 0x03; m_chr_mode = data & 0x03;
m_ex1_chr = 0; m_ex1_chr = 0;
m_split_chr = 0; m_split_chr = 0;
//LOG_MMC(("MMC5 vrom bank mode: %02x\n", data)); //LOG_MMC(("MMC5 vrom bank mode: %02x\n", data));
break; break;
case 0x1102: case 0x1102:
m_wram_protect_1 = data & 0x03; m_wram_protect_1 = data & 0x03;
LOG_MMC(("MMC5 vram protect 1: %02x\n", data)); LOG_MMC(("MMC5 vram protect 1: %02x\n", data));
break; break;
case 0x1103:
m_wram_protect_2 = data & 0x03;
LOG_MMC(("MMC5 vram protect 2: %02x\n", data));
break;
case 0x1104: // Extra VRAM (EXRAM) case 0x1103:
m_exram_control = data & 0x03; m_wram_protect_2 = data & 0x03;
LOG_MMC(("MMC5 exram control: %02x\n", data)); LOG_MMC(("MMC5 vram protect 2: %02x\n", data));
break; break;
case 0x1105: case 0x1104: // Extra VRAM (EXRAM)
set_mirror(0, (data & 0x03) >> 0); m_exram_control = data & 0x03;
set_mirror(1, (data & 0x0c) >> 2); LOG_MMC(("MMC5 exram control: %02x\n", data));
set_mirror(2, (data & 0x30) >> 4); break;
set_mirror(3, (data & 0xc0) >> 6);
break;
case 0x1106: case 0x1105:
m_floodtile = data; set_mirror(0, (data & 0x03) >> 0);
break; set_mirror(1, (data & 0x0c) >> 2);
set_mirror(2, (data & 0x30) >> 4);
set_mirror(3, (data & 0xc0) >> 6);
break;
case 0x1107: case 0x1106:
m_floodattr = m_mmc5_attrib[data & 3]; m_floodtile = data;
break; break;
case 0x1113: case 0x1107:
LOG_MMC(("MMC5 mid RAM bank select: %02x\n", data & 0x07)); m_floodattr = m_mmc5_attrib[data & 3];
m_wram_base = data & 0x07; break;
break;
case 0x1113:
LOG_MMC(("MMC5 mid RAM bank select: %02x\n", data & 0x07));
m_wram_base = data & 0x07;
break;
case 0x1114: case 0x1114:
case 0x1115: case 0x1115:
case 0x1116: case 0x1116:
case 0x1117: case 0x1117:
m_prg_regs[offset & 3] = data & 0x7f; m_prg_regs[offset & 3] = data & 0x7f;
m_prg_ram_mapped[offset & 3] = !BIT(data, 7); // m_prg_ram_mapped[3] is not used, in fact! m_prg_ram_mapped[offset & 3] = !BIT(data, 7); // m_prg_ram_mapped[3] is not used, in fact!
update_prg(); update_prg();
break; break;
case 0x1120: case 0x1120:
case 0x1121: case 0x1121:
case 0x1122: case 0x1122:
case 0x1123: case 0x1123:
case 0x1124: case 0x1124:
case 0x1125: case 0x1125:
case 0x1126: case 0x1126:
case 0x1127: case 0x1127:
m_vrom_bank[offset & 0x07] = data | (m_high_chr << 8); m_vrom_bank[offset & 0x07] = data | (m_high_chr << 8);
m_last_chr = LAST_CHR_REG_A; m_last_chr = LAST_CHR_REG_A;
m_ex1_chr = 0; m_ex1_chr = 0;
m_split_chr = 0; m_split_chr = 0;
break; break;
case 0x1128: case 0x1128:
case 0x1129: case 0x1129:
case 0x112a: case 0x112a:
case 0x112b: case 0x112b:
m_vrom_bank[offset & 0x0f] = data | (m_high_chr << 8); m_vrom_bank[offset & 0x0f] = data | (m_high_chr << 8);
m_last_chr = LAST_CHR_REG_B; m_last_chr = LAST_CHR_REG_B;
m_ex1_chr = 0; m_ex1_chr = 0;
m_split_chr = 0; m_split_chr = 0;
break; break;
case 0x1130: case 0x1130:
m_high_chr = data & 0x03; m_high_chr = data & 0x03;
m_ex1_chr = 0; m_ex1_chr = 0;
m_split_chr = 0; m_split_chr = 0;
break; break;
case 0x1200:
// in EX2 and EX3 modes, no split screen
m_split_scr = BIT(data, 7);
m_split_rev = BIT(data, 6);
m_split_ctrl = data & 0x1f;
break;
case 0x1200: case 0x1201:
// in EX2 and EX3 modes, no split screen m_split_yst = (data >= 240) ? data - 16 : data;
m_split_scr = BIT(data, 7); break;
m_split_rev = BIT(data, 6);
m_split_ctrl = data & 0x1f;
break;
case 0x1201: case 0x1202:
m_split_yst = (data >= 240) ? data - 16 : data; m_split_bank = data;
break; m_split_chr = 1;
break;
case 0x1202: case 0x1203:
m_split_bank = data; m_irq_count = data;
m_split_chr = 1; LOG_MMC(("MMC5 irq scanline: %d\n", m_irq_count));
break; break;
case 0x1203: case 0x1204:
m_irq_count = data; m_irq_enable = data & 0x80;
LOG_MMC(("MMC5 irq scanline: %d\n", m_irq_count)); LOG_MMC(("MMC5 irq enable: %02x\n", data));
break; break;
case 0x1204:
m_irq_enable = data & 0x80;
LOG_MMC(("MMC5 irq enable: %02x\n", data));
break;
case 0x1205:
m_mult1 = data;
break;
case 0x1206:
m_mult2 = data;
break;
default: case 0x1205:
logerror("MMC5 uncaught write, offset: %04x, data: %02x\n", offset + 0x4100, data); m_mult1 = data;
break; break;
case 0x1206:
m_mult2 = data;
break;
default:
logerror("MMC5 uncaught write, offset: %04x, data: %02x\n", offset + 0x4100, data);
break;
} }
} }

View File

@ -75,6 +75,7 @@ protected:
uint8_t m_prg_ram_mapped[4]; uint8_t m_prg_ram_mapped[4];
uint8_t m_ex1_bank; uint8_t m_ex1_bank;
uint8_t m_ex1_attrib;
uint8_t m_high_chr; // $5130 uint8_t m_high_chr; // $5130

View File

@ -676,6 +676,9 @@ void ppu2c0x_device::draw_background(uint8_t *line_priority)
index1 = tile_index + x; index1 = tile_index + x;
// page2 is the output of the nametable read (this section is the FIRST read per tile!)
page2 = readbyte(index1);
// this is attribute table stuff! (actually read 2 in PPUspeak)! // this is attribute table stuff! (actually read 2 in PPUspeak)!
/* Figure out which byte in the color table to use */ /* Figure out which byte in the color table to use */
pos = ((index1 & 0x380) >> 4) | ((index1 & 0x1f) >> 2); pos = ((index1 & 0x380) >> 4) | ((index1 & 0x1f) >> 2);
@ -686,10 +689,6 @@ void ppu2c0x_device::draw_background(uint8_t *line_priority)
/* figure out which bits in the color table to use */ /* figure out which bits in the color table to use */
color_bits = ((index1 & 0x40) >> 4) + (index1 & 0x02); color_bits = ((index1 & 0x40) >> 4) + (index1 & 0x02);
// page2 is the output of the nametable read (this section is the FIRST read per tile!)
address = index1 & 0x3ff;
page2 = readbyte(index1);
// 27/12/2002 // 27/12/2002
if (!m_latch.isnull()) if (!m_latch.isnull())
m_latch((m_tile_page << 10) | (page2 << 4)); m_latch((m_tile_page << 10) | (page2 << 4));