diff options
Diffstat (limited to 'src/mess/machine/nes_konami.c')
-rw-r--r-- | src/mess/machine/nes_konami.c | 680 |
1 files changed, 680 insertions, 0 deletions
diff --git a/src/mess/machine/nes_konami.c b/src/mess/machine/nes_konami.c new file mode 100644 index 00000000000..04991bceaff --- /dev/null +++ b/src/mess/machine/nes_konami.c @@ -0,0 +1,680 @@ +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Konami PCBs + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + + + Here we emulate the following PCBs (multiple mappers needed due to different wirings in the pcbs + causing different addresses to be used for bankswitch & irq) + + * Konami VRC-1 [mapper 75] + * Konami VRC-2 [mapper 22,23,25] + * Konami VRC-3 [mapper 73] + * Konami VRC-4 [mapper 21,23,25] + * Konami VRC-6 [mapper 24,26] + * Konami VRC-7 [mapper 85] + + + TODO: + - improve IRQ (CPU mode not currently emulated) + - add VRC-6 sound features + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "machine/nes_konami.h" + +#include "cpu/m6502/m6502.h" +#include "sound/2413intf.h" + +#ifdef NES_PCB_DEBUG +#define VERBOSE 1 +#else +#define VERBOSE 0 +#endif + +#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0) + + +//------------------------------------------------- +// constructor +//------------------------------------------------- + +const device_type NES_VRC1 = &device_creator<nes_konami_vrc1_device>; +const device_type NES_VRC2 = &device_creator<nes_konami_vrc2_device>; +const device_type NES_VRC3 = &device_creator<nes_konami_vrc3_device>; +const device_type NES_VRC4 = &device_creator<nes_konami_vrc4_device>; +const device_type NES_VRC6 = &device_creator<nes_konami_vrc6_device>; +const device_type NES_VRC7 = &device_creator<nes_konami_vrc7_device>; + + +nes_konami_vrc1_device::nes_konami_vrc1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_VRC1, "NES Cart Konami VRC-1 PCB", tag, owner, clock, "nes_vrc1", __FILE__) +{ +} + +nes_konami_vrc2_device::nes_konami_vrc2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_VRC2, "NES Cart Konami VRC-2 PCB", tag, owner, clock, "nes_vrc2", __FILE__) +{ +} + +nes_konami_vrc3_device::nes_konami_vrc3_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : nes_nrom_device(mconfig, type, name, tag, owner, clock, shortname, source) +{ +} + +nes_konami_vrc3_device::nes_konami_vrc3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_VRC3, "NES Cart Konami VRC-3 PCB", tag, owner, clock, "nes_vrc3", __FILE__) +{ +} + +nes_konami_vrc4_device::nes_konami_vrc4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_konami_vrc3_device(mconfig, NES_VRC4, "NES Cart Konami VRC-4 PCB", tag, owner, clock, "nes_vrc4", __FILE__) +{ +} + +nes_konami_vrc6_device::nes_konami_vrc6_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_konami_vrc3_device(mconfig, NES_VRC6, "NES Cart Konami VRC-6 PCB", tag, owner, clock, "nes_vrc6", __FILE__) +{ +} + +nes_konami_vrc7_device::nes_konami_vrc7_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_konami_vrc3_device(mconfig, NES_VRC7, "NES Cart Konami VRC-7 PCB", tag, owner, clock, "nes_vrc7", __FILE__) +{ +} + + + +void nes_konami_vrc1_device::device_start() +{ + common_start(); + save_item(NAME(m_mmc_vrom_bank)); +} + +void nes_konami_vrc1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(m_prg_chunks - 1); + chr8(0, m_chr_source); + + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); +} + +void nes_konami_vrc2_device::device_start() +{ + common_start(); + save_item(NAME(m_mmc_vrom_bank)); + save_item(NAME(m_latch)); +} + +void nes_konami_vrc2_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(m_prg_chunks - 1); + chr8(0, m_chr_source); + + m_latch = 0; + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); +} + +void nes_konami_vrc3_device::device_start() +{ + common_start(); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_enable_latch)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); +} + +void nes_konami_vrc3_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(m_prg_chunks - 1); + chr8(0, m_chr_source); + + m_irq_enable = 0; + m_irq_enable_latch = 0; + m_irq_count = 0; + m_irq_count_latch = 0; +} + + +void nes_konami_vrc4_device::device_start() +{ + common_start(); + save_item(NAME(m_irq_mode)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_enable_latch)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); + save_item(NAME(m_latch)); + save_item(NAME(m_mmc_prg_bank)); + save_item(NAME(m_mmc_vrom_bank)); +} + +void nes_konami_vrc4_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(m_prg_chunks - 1); + chr8(0, m_chr_source); + + m_irq_mode = 0; + m_irq_enable = 0; + m_irq_enable_latch = 0; + m_irq_count = 0; + m_irq_count_latch = 0; + + m_latch = 0; + m_mmc_prg_bank = 0; + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); +} + +void nes_konami_vrc7_device::device_start() +{ + m_ym2413 = device().subdevice("ym"); + + common_start(); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_enable_latch)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); +} + +void nes_konami_vrc7_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg8_89(0); + prg8_ab(0); + prg8_cd(0); + prg8_ef(0xff); + chr8(0, m_chr_source); + + m_irq_enable = 0; + m_irq_enable_latch = 0; + m_irq_count = 0; + m_irq_count_latch = 0; +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Konami VRC1 and Jaleco JF20, JF22 + + Games: Exciting Boxing, Ganbare Goemon!, Tetsuwan Atom + + iNES: mapper 75 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_konami_vrc1_device::write_h) +{ + LOG_MMC(("VRC-1 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x0000: + prg8_89(data); + break; + case 0x2000: + prg8_ab(data); + break; + case 0x4000: + prg8_cd(data); + break; + case 0x1000: + set_nt_mirroring((data & 0x01) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + m_mmc_vrom_bank[0] = (m_mmc_vrom_bank[0] & 0x0f) | ((data & 0x02) << 3); + m_mmc_vrom_bank[1] = (m_mmc_vrom_bank[1] & 0x0f) | ((data & 0x04) << 2); + chr4_0(m_mmc_vrom_bank[0], CHRROM); + chr4_4(m_mmc_vrom_bank[1], CHRROM); + break; + case 0x6000: + m_mmc_vrom_bank[0] = (m_mmc_vrom_bank[0] & 0x10) | (data & 0x0f); + chr4_0(m_mmc_vrom_bank[0], CHRROM); + break; + case 0x7000: + m_mmc_vrom_bank[1] = (m_mmc_vrom_bank[1] & 0x10) | (data & 0x0f); + chr4_4(m_mmc_vrom_bank[1], CHRROM); + break; + } +} + +/*------------------------------------------------- + + Konami VRC-2 + + In MESS: Supported. + + -------------------------------------------------*/ + +READ8_MEMBER(nes_konami_vrc2_device::read_m) +{ + LOG_MMC(("VRC-2 read_m, offset: %04x\n", offset)); + + if (m_battery) + return m_battery[offset & (m_battery_size - 1)]; + else if (m_prgram) + return m_prgram[offset & (m_prgram_size - 1)]; + else // sort of protection? it returns open bus in $7000-$7fff and (open bus & 0xfe) | m_latch in $6000-$6fff + return (offset < 0x1000) ? ((((offset + 0x8000) & 0xfe00) >> 8) | (m_latch & 1)) : (((offset + 0x8000) & 0xff00) >> 8); +} + +WRITE8_MEMBER(nes_konami_vrc2_device::write_m) +{ + LOG_MMC(("VRC-2 write_m, offset: %04x, data: %02x\n", offset, data)); + + if (m_battery) + m_battery[offset & (m_battery_size - 1)] = data; + else if (m_prgram) + m_prgram[offset & (m_prgram_size - 1)] = data; + else if (offset < 0x1000) + m_latch = data; +} + +WRITE8_MEMBER(nes_konami_vrc2_device::write_h) +{ + UINT8 bank, shift, mask; + UINT16 add_lines = ((offset << (9 - m_vrc_ls_prg_a)) & 0x200) | ((offset << (8 - m_vrc_ls_prg_b)) & 0x100); + LOG_MMC(("VRC-2 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x0000: + prg8_89(data); + break; + case 0x2000: + prg8_ab(data); + break; + case 0x1000: + switch (data & 0x03) + { + case 0x00: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 0x01: set_nt_mirroring(PPU_MIRROR_HORZ); break; + case 0x02: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 0x03: set_nt_mirroring(PPU_MIRROR_HIGH); break; + } + break; + case 0x3000: + case 0x4000: + case 0x5000: + case 0x6000: + bank = ((offset & 0x7000) - 0x3000) / 0x0800 + BIT(add_lines, 9); + shift = BIT(add_lines, 8) * 4; + mask = (0xf0 >> shift); + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & mask) | (((data >> m_vrc_ls_chr) & 0x0f) << shift); + chr1_x(bank, m_mmc_vrom_bank[bank], CHRROM); + break; + default: + logerror("VRC-2 write_h uncaught write, addr: %04x value: %02x\n", offset + 0x8000, data); + break; + } +} + +/*------------------------------------------------- + + Konami VRC3 + + Games: Salamander + + iNES: mapper 73 + + In MESS: Supported. It also uses konami_irq. + + -------------------------------------------------*/ + +void nes_konami_vrc3_device::hblank_irq(int scanline, int vblank, int blanked) +{ + /* Increment & check the IRQ scanline counter */ + if (m_irq_enable && (++m_irq_count == 0x100)) + { + m_irq_count = m_irq_count_latch; + m_irq_enable = m_irq_enable_latch; + machine().device("maincpu")->execute().set_input_line(M6502_IRQ_LINE, HOLD_LINE); + } +} + +WRITE8_MEMBER(nes_konami_vrc3_device::write_h) +{ + LOG_MMC(("VRC-3 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x0000: + case 0x1000: + /* dunno which address controls these */ + m_irq_count_latch = data; + m_irq_enable_latch = data; + break; + case 0x2000: + m_irq_enable = data; + break; + case 0x3000: + m_irq_count &= ~0x0f; + m_irq_count |= data & 0x0f; + break; + case 0x4000: + m_irq_count &= ~0xf0; + m_irq_count |= (data & 0x0f) << 4; + break; + case 0x7000: + prg16_89ab(data); + break; + default: + logerror("VRC-3 write_h uncaught write, offset %04x, data: %02x\n", offset, data); + break; + } +} + +/*------------------------------------------------- + + Konami VRC-4 + + In MESS: Supported + + -------------------------------------------------*/ + +void nes_konami_vrc4_device::set_prg() +{ + if (m_latch & 0x02) + { + prg8_89(0xfe); + prg8_cd(m_mmc_prg_bank); + } + else + { + prg8_89(m_mmc_prg_bank); + prg8_cd(0xfe); + } +} + +WRITE8_MEMBER(nes_konami_vrc4_device::write_h) +{ + UINT8 bank, shift, mask; + UINT16 add_lines = ((offset << (9 - m_vrc_ls_prg_a)) & 0x200) | ((offset << (8 - m_vrc_ls_prg_b)) & 0x100); + LOG_MMC(("VRC-4 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x0000: + m_mmc_prg_bank = data; + set_prg(); + break; + case 0x2000: + prg8_ab(data); + break; + case 0x1000: + if (add_lines & 0x200) + { + m_latch = data & 0x02; + set_prg(); + } + else + { + switch (data & 0x03) + { + case 0x00: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 0x01: set_nt_mirroring(PPU_MIRROR_HORZ); break; + case 0x02: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 0x03: set_nt_mirroring(PPU_MIRROR_HIGH); break; + } + } + break; + case 0x3000: + case 0x4000: + case 0x5000: + case 0x6000: + bank = ((offset & 0x7000) - 0x3000) / 0x0800 + BIT(add_lines, 9); + shift = BIT(add_lines, 8) * 4; + mask = (0xf0 >> shift); + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & mask) | ((data & 0x0f) << shift); + chr1_x(bank, m_mmc_vrom_bank[bank], CHRROM); + break; + case 0x7000: + switch (add_lines) + { + case 0x000: + m_irq_count_latch = (m_irq_count_latch & 0xf0) | (data & 0x0f); + break; + case 0x100: + m_irq_count_latch = (m_irq_count_latch & 0x0f) | ((data & 0x0f) << 4); + break; + case 0x200: + m_irq_mode = data & 0x04; // currently not implemented: 0 = prescaler mode / 1 = CPU mode + m_irq_enable = data & 0x02; + m_irq_enable_latch = data & 0x01; + if (data & 0x02) + m_irq_count = m_irq_count_latch; + break; + case 0x300: + m_irq_enable = m_irq_enable_latch; + break; + } + break; + default: + logerror("VRC-4 write_h uncaught write, addr: %04x value: %02x\n", ((offset & 0x7000) | add_lines) + 0x8000, data); + break; + } +} + +/*------------------------------------------------- + + Konami VRC-6 + + In MESS: Supported. It also uses konami_irq (there are IRQ + issues though: see Akumajou Densetsu intro). + + TODO: Add sound capabilities support! + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_konami_vrc6_device::write_h) +{ + UINT8 bank; + UINT16 add_lines = ((offset << (9 - m_vrc_ls_prg_a)) & 0x200) | ((offset << (8 - m_vrc_ls_prg_b)) & 0x100); + LOG_MMC(("VRC-6 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x0000: + prg16_89ab(data); + break; + case 0x4000: + prg8_cd(data); + break; + case 0x1000: + case 0x2000: + LOG_MMC(("Konami VRC-6 Sound write, offset: %04x, data: %02x\n", (offset & 0x7000) | add_lines, data)); + break; + case 0x3000: + if (add_lines == 0x300) + { + switch (data & 0x0c) + { + case 0x00: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 0x04: set_nt_mirroring(PPU_MIRROR_HORZ); break; + case 0x08: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 0x0c: set_nt_mirroring(PPU_MIRROR_HIGH); break; + } + } + else + LOG_MMC(("Konami VRC-6 Sound write, offset: %04x, data: %02x\n", (offset & 0x7000) | add_lines, data)); + break; + case 0x5000: + case 0x6000: + bank = ((offset & 0x7000) - 0x5000) / 0x0400 + ((add_lines & 0x0300) >> 8); + chr1_x(bank, data, CHRROM); + break; + case 0x7000: + switch (add_lines) + { + case 0x000: + m_irq_count_latch = data; + break; + case 0x100: + m_irq_mode = data & 0x04; // currently not implemented: 0 = prescaler mode / 1 = CPU mode + m_irq_enable = data & 0x02; + m_irq_enable_latch = data & 0x01; + if (data & 0x02) + m_irq_count = m_irq_count_latch; + break; + case 0x200: + m_irq_enable = m_irq_enable_latch; + break; + default: + logerror("VRC-6 write_h uncaught write, addr: %04x value: %02x\n", ((offset & 0x7000) | add_lines) + 0x8000, data); + break; + } + break; + default: + logerror("VRC-6 write_h uncaught write, addr: %04x value: %02x\n", ((offset & 0x7000) | add_lines) + 0x8000, data); + break; + } +} + +/*------------------------------------------------- + + Konami VRC7 + + Games: Lagrange Point, Tiny Toon Adventures 2 + + iNES: mapper 85 + + In MESS: Supported. It also uses konami_irq. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_konami_vrc7_device::write_h) +{ + UINT8 bank; + LOG_MMC(("VRC-7 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7038) + { + case 0x0000: + prg8_89(data); + break; + case 0x0008: + case 0x0010: + case 0x0018: + prg8_ab(data); + break; + + case 0x1000: + prg8_cd(data); + break; + + case 0x1010: + case 0x1018: + ym2413_register_port_w(m_ym2413, space, 0, data); + break; + case 0x1030: + case 0x1038: + ym2413_data_port_w(m_ym2413, space, 0, data); + break; + + case 0x2000: + case 0x2008: + case 0x2010: + case 0x2018: + case 0x3000: + case 0x3008: + case 0x3010: + case 0x3018: + case 0x4000: + case 0x4008: + case 0x4010: + case 0x4018: + case 0x5000: + case 0x5008: + case 0x5010: + case 0x5018: + bank = ((offset & 0x7000) - 0x2000) / 0x0800 + ((offset & 0x0018) ? 1 : 0); + chr1_x(bank, data, m_chr_source); + break; + + case 0x6000: + switch (data & 0x03) + { + case 0x00: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 0x01: set_nt_mirroring(PPU_MIRROR_HORZ); break; + case 0x02: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 0x03: set_nt_mirroring(PPU_MIRROR_HIGH); break; + } + break; + case 0x6008: case 0x6010: case 0x6018: + m_irq_count_latch = data; + break; + case 0x7000: + m_irq_mode = data & 0x04; // currently not implemented: 0 = prescaler mode / 1 = CPU mode + m_irq_enable = data & 0x02; + m_irq_enable_latch = data & 0x01; + if (data & 0x02) + m_irq_count = m_irq_count_latch; + break; + case 0x7008: case 0x7010: case 0x7018: + m_irq_enable = m_irq_enable_latch; + break; + + default: + logerror("VRC-7 write_h uncaught write, addr: %04x value: %02x\n", offset + 0x8000, data); + break; + } +} + + +//------------------------------------------------- +// MACHINE_DRIVER( vrc7 ) +//------------------------------------------------- + +// From NESdev wiki: The VRC7, in addition to being a mapper chip, also produces 6 channels of +// 2-operator FM Synthesis Audio. It is a derivative of the Yamaha YM2413 OPLL, implementing a +// subset of its features and containing a custom fixed patch set. +// The synthesis core is related to the Yamaha YM2413 OPLL: +// - Register layout is the same. +// - VRC7 has 6 channels, OPLL has 9. +// - VRC7 has no rhythm channels, OPLL does (the last 3 channels are either FM or Rhythm on OPLL). +// - VRC7 built-in instruments are not the same as OPLL instruments. +// - VRC7 has no readily-accessible status register, under normal circumstances it is write-only; +// OPLL has an undocumented, 2-bit 'internal state' register. +// - VRC7 has an internal state output pin (may be serial version of the 2 bit internal state register) +// and has one output pin for audio, multiplexed for all 6 channels; OPLL has two output pins, one for +// FM and one for Rhythm, and has no special status pin. + +// FIXME: we currently emulate this as a base YM2413! + + +#define VRC7_NTSC_CLOCK (21477272.724 / 12) + +static MACHINE_CONFIG_FRAGMENT( vrc7 ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("ym", YM2413, VRC7_NTSC_CLOCK) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.5) +MACHINE_CONFIG_END + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor nes_konami_vrc7_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( vrc7 ); +} |