From f88cefad27a1737c76e09d99c9fb43e173506081 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Sun, 13 Sep 2015 08:41:44 +0200 Subject: Move all devices into separate part of src tree (nw) --- src/devices/bus/nes/2a03pur.c | 109 ++ src/devices/bus/nes/2a03pur.h | 33 + src/devices/bus/nes/act53.c | 206 +++ src/devices/bus/nes/act53.h | 38 + src/devices/bus/nes/aladdin.c | 339 +++++ src/devices/bus/nes/aladdin.h | 167 +++ src/devices/bus/nes/ave.c | 214 +++ src/devices/bus/nes/ave.h | 69 + src/devices/bus/nes/bandai.c | 479 +++++++ src/devices/bus/nes/bandai.h | 141 ++ src/devices/bus/nes/benshieng.c | 105 ++ src/devices/bus/nes/benshieng.h | 34 + src/devices/bus/nes/bootleg.c | 1635 ++++++++++++++++++++++ src/devices/bus/nes/bootleg.h | 480 +++++++ src/devices/bus/nes/camerica.c | 225 ++++ src/devices/bus/nes/camerica.h | 72 + src/devices/bus/nes/cne.c | 220 +++ src/devices/bus/nes/cne.h | 66 + src/devices/bus/nes/cony.c | 397 ++++++ src/devices/bus/nes/cony.h | 75 ++ src/devices/bus/nes/datach.c | 420 ++++++ src/devices/bus/nes/datach.h | 170 +++ src/devices/bus/nes/discrete.c | 209 +++ src/devices/bus/nes/discrete.h | 80 ++ src/devices/bus/nes/disksys.c | 431 ++++++ src/devices/bus/nes/disksys.h | 71 + src/devices/bus/nes/event.c | 239 ++++ src/devices/bus/nes/event.h | 44 + src/devices/bus/nes/ggenie.c | 274 ++++ src/devices/bus/nes/ggenie.h | 58 + src/devices/bus/nes/henggedianzi.c | 278 ++++ src/devices/bus/nes/henggedianzi.h | 78 ++ src/devices/bus/nes/hes.c | 87 ++ src/devices/bus/nes/hes.h | 28 + src/devices/bus/nes/hosenkan.c | 155 +++ src/devices/bus/nes/hosenkan.h | 39 + src/devices/bus/nes/irem.c | 352 +++++ src/devices/bus/nes/irem.h | 108 ++ src/devices/bus/nes/jaleco.c | 886 ++++++++++++ src/devices/bus/nes/jaleco.h | 256 ++++ src/devices/bus/nes/jy.c | 589 ++++++++ src/devices/bus/nes/jy.h | 104 ++ src/devices/bus/nes/kaiser.c | 588 ++++++++ src/devices/bus/nes/kaiser.h | 184 +++ src/devices/bus/nes/karastudio.c | 309 +++++ src/devices/bus/nes/karastudio.h | 147 ++ src/devices/bus/nes/konami.c | 787 +++++++++++ src/devices/bus/nes/konami.h | 155 +++ src/devices/bus/nes/legacy.c | 311 +++++ src/devices/bus/nes/legacy.h | 81 ++ src/devices/bus/nes/mmc1.c | 374 ++++++ src/devices/bus/nes/mmc1.h | 82 ++ src/devices/bus/nes/mmc2.c | 198 +++ src/devices/bus/nes/mmc2.h | 54 + src/devices/bus/nes/mmc3.c | 560 ++++++++ src/devices/bus/nes/mmc3.h | 146 ++ src/devices/bus/nes/mmc3_clones.c | 2588 +++++++++++++++++++++++++++++++++++ src/devices/bus/nes/mmc3_clones.h | 697 ++++++++++ src/devices/bus/nes/mmc5.c | 679 ++++++++++ src/devices/bus/nes/mmc5.h | 94 ++ src/devices/bus/nes/multigame.c | 2602 ++++++++++++++++++++++++++++++++++++ src/devices/bus/nes/multigame.h | 894 +++++++++++++ src/devices/bus/nes/namcot.c | 688 ++++++++++ src/devices/bus/nes/namcot.h | 163 +++ src/devices/bus/nes/nanjing.c | 175 +++ src/devices/bus/nes/nanjing.h | 38 + src/devices/bus/nes/nes_carts.c | 366 +++++ src/devices/bus/nes/nes_carts.h | 73 + src/devices/bus/nes/nes_ines.inc | 939 +++++++++++++ src/devices/bus/nes/nes_pcb.inc | 609 +++++++++ src/devices/bus/nes/nes_slot.c | 1053 +++++++++++++++ src/devices/bus/nes/nes_slot.h | 430 ++++++ src/devices/bus/nes/nes_unif.inc | 575 ++++++++ src/devices/bus/nes/ntdec.c | 186 +++ src/devices/bus/nes/ntdec.h | 51 + src/devices/bus/nes/nxrom.c | 583 ++++++++ src/devices/bus/nes/nxrom.h | 216 +++ src/devices/bus/nes/pirate.c | 1387 +++++++++++++++++++ src/devices/bus/nes/pirate.h | 399 ++++++ src/devices/bus/nes/pt554.c | 107 ++ src/devices/bus/nes/pt554.h | 31 + src/devices/bus/nes/racermate.c | 92 ++ src/devices/bus/nes/racermate.h | 35 + src/devices/bus/nes/rcm.c | 303 +++++ src/devices/bus/nes/rcm.h | 105 ++ src/devices/bus/nes/rexsoft.c | 262 ++++ src/devices/bus/nes/rexsoft.h | 61 + src/devices/bus/nes/sachen.c | 695 ++++++++++ src/devices/bus/nes/sachen.h | 261 ++++ src/devices/bus/nes/somari.c | 421 ++++++ src/devices/bus/nes/somari.h | 54 + src/devices/bus/nes/sunsoft.c | 628 +++++++++ src/devices/bus/nes/sunsoft.h | 147 ++ src/devices/bus/nes/sunsoft_dcs.c | 315 +++++ src/devices/bus/nes/sunsoft_dcs.h | 150 +++ src/devices/bus/nes/taito.c | 470 +++++++ src/devices/bus/nes/taito.h | 104 ++ src/devices/bus/nes/tengen.c | 376 ++++++ src/devices/bus/nes/tengen.h | 88 ++ src/devices/bus/nes/txc.c | 268 ++++ src/devices/bus/nes/txc.h | 99 ++ src/devices/bus/nes/waixing.c | 1244 +++++++++++++++++ src/devices/bus/nes/waixing.h | 396 ++++++ 103 files changed, 36233 insertions(+) create mode 100644 src/devices/bus/nes/2a03pur.c create mode 100644 src/devices/bus/nes/2a03pur.h create mode 100644 src/devices/bus/nes/act53.c create mode 100644 src/devices/bus/nes/act53.h create mode 100644 src/devices/bus/nes/aladdin.c create mode 100644 src/devices/bus/nes/aladdin.h create mode 100644 src/devices/bus/nes/ave.c create mode 100644 src/devices/bus/nes/ave.h create mode 100644 src/devices/bus/nes/bandai.c create mode 100644 src/devices/bus/nes/bandai.h create mode 100644 src/devices/bus/nes/benshieng.c create mode 100644 src/devices/bus/nes/benshieng.h create mode 100644 src/devices/bus/nes/bootleg.c create mode 100644 src/devices/bus/nes/bootleg.h create mode 100644 src/devices/bus/nes/camerica.c create mode 100644 src/devices/bus/nes/camerica.h create mode 100644 src/devices/bus/nes/cne.c create mode 100644 src/devices/bus/nes/cne.h create mode 100644 src/devices/bus/nes/cony.c create mode 100644 src/devices/bus/nes/cony.h create mode 100644 src/devices/bus/nes/datach.c create mode 100644 src/devices/bus/nes/datach.h create mode 100644 src/devices/bus/nes/discrete.c create mode 100644 src/devices/bus/nes/discrete.h create mode 100644 src/devices/bus/nes/disksys.c create mode 100644 src/devices/bus/nes/disksys.h create mode 100644 src/devices/bus/nes/event.c create mode 100644 src/devices/bus/nes/event.h create mode 100644 src/devices/bus/nes/ggenie.c create mode 100644 src/devices/bus/nes/ggenie.h create mode 100644 src/devices/bus/nes/henggedianzi.c create mode 100644 src/devices/bus/nes/henggedianzi.h create mode 100644 src/devices/bus/nes/hes.c create mode 100644 src/devices/bus/nes/hes.h create mode 100644 src/devices/bus/nes/hosenkan.c create mode 100644 src/devices/bus/nes/hosenkan.h create mode 100644 src/devices/bus/nes/irem.c create mode 100644 src/devices/bus/nes/irem.h create mode 100644 src/devices/bus/nes/jaleco.c create mode 100644 src/devices/bus/nes/jaleco.h create mode 100644 src/devices/bus/nes/jy.c create mode 100644 src/devices/bus/nes/jy.h create mode 100644 src/devices/bus/nes/kaiser.c create mode 100644 src/devices/bus/nes/kaiser.h create mode 100644 src/devices/bus/nes/karastudio.c create mode 100644 src/devices/bus/nes/karastudio.h create mode 100644 src/devices/bus/nes/konami.c create mode 100644 src/devices/bus/nes/konami.h create mode 100644 src/devices/bus/nes/legacy.c create mode 100644 src/devices/bus/nes/legacy.h create mode 100644 src/devices/bus/nes/mmc1.c create mode 100644 src/devices/bus/nes/mmc1.h create mode 100644 src/devices/bus/nes/mmc2.c create mode 100644 src/devices/bus/nes/mmc2.h create mode 100644 src/devices/bus/nes/mmc3.c create mode 100644 src/devices/bus/nes/mmc3.h create mode 100644 src/devices/bus/nes/mmc3_clones.c create mode 100644 src/devices/bus/nes/mmc3_clones.h create mode 100644 src/devices/bus/nes/mmc5.c create mode 100644 src/devices/bus/nes/mmc5.h create mode 100644 src/devices/bus/nes/multigame.c create mode 100644 src/devices/bus/nes/multigame.h create mode 100644 src/devices/bus/nes/namcot.c create mode 100644 src/devices/bus/nes/namcot.h create mode 100644 src/devices/bus/nes/nanjing.c create mode 100644 src/devices/bus/nes/nanjing.h create mode 100644 src/devices/bus/nes/nes_carts.c create mode 100644 src/devices/bus/nes/nes_carts.h create mode 100644 src/devices/bus/nes/nes_ines.inc create mode 100644 src/devices/bus/nes/nes_pcb.inc create mode 100644 src/devices/bus/nes/nes_slot.c create mode 100644 src/devices/bus/nes/nes_slot.h create mode 100644 src/devices/bus/nes/nes_unif.inc create mode 100644 src/devices/bus/nes/ntdec.c create mode 100644 src/devices/bus/nes/ntdec.h create mode 100644 src/devices/bus/nes/nxrom.c create mode 100644 src/devices/bus/nes/nxrom.h create mode 100644 src/devices/bus/nes/pirate.c create mode 100644 src/devices/bus/nes/pirate.h create mode 100644 src/devices/bus/nes/pt554.c create mode 100644 src/devices/bus/nes/pt554.h create mode 100644 src/devices/bus/nes/racermate.c create mode 100644 src/devices/bus/nes/racermate.h create mode 100644 src/devices/bus/nes/rcm.c create mode 100644 src/devices/bus/nes/rcm.h create mode 100644 src/devices/bus/nes/rexsoft.c create mode 100644 src/devices/bus/nes/rexsoft.h create mode 100644 src/devices/bus/nes/sachen.c create mode 100644 src/devices/bus/nes/sachen.h create mode 100644 src/devices/bus/nes/somari.c create mode 100644 src/devices/bus/nes/somari.h create mode 100644 src/devices/bus/nes/sunsoft.c create mode 100644 src/devices/bus/nes/sunsoft.h create mode 100644 src/devices/bus/nes/sunsoft_dcs.c create mode 100644 src/devices/bus/nes/sunsoft_dcs.h create mode 100644 src/devices/bus/nes/taito.c create mode 100644 src/devices/bus/nes/taito.h create mode 100644 src/devices/bus/nes/tengen.c create mode 100644 src/devices/bus/nes/tengen.h create mode 100644 src/devices/bus/nes/txc.c create mode 100644 src/devices/bus/nes/txc.h create mode 100644 src/devices/bus/nes/waixing.c create mode 100644 src/devices/bus/nes/waixing.h (limited to 'src/devices/bus/nes') diff --git a/src/devices/bus/nes/2a03pur.c b/src/devices/bus/nes/2a03pur.c new file mode 100644 index 00000000000..d7c807b63f2 --- /dev/null +++ b/src/devices/bus/nes/2a03pur.c @@ -0,0 +1,109 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for 2A03 Puritans Album + + + Here we emulate the PCB designed by infiniteneslives and + rainwarrior for this homebew multicart [mapper 30?] + The main difference of this PCB compared to others is that it + uses 4k PRG banks! + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "2a03pur.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_2A03PURITANS = &device_creator; + + +nes_2a03pur_device::nes_2a03pur_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_2A03PURITANS, "NES Cart 2A03 Puritans Album PCB", tag, owner, clock, "nes_2a03pur", __FILE__) +{ +} + + + +void nes_2a03pur_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); + memset(m_reg, 0x00, sizeof(m_reg)); + m_reg[7] = 0xff; +} + +void nes_2a03pur_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + chr8(0, m_chr_source); + // register content is not touched by reset +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Board 2A03 Puritans Album + + In MESS: supported. + + This mapper supports up to 1MB of PRG-ROM, in 4k + banks located at $8000, $9000, $A000, $B000, $C000, + $D000, $E000, and $F000. Each bank is selected by n + 8-bit register at $5FF8, $5FF9, $5FFA, $5FFB, $5FFC, + $5FFD, $5FFE, and $5FFF, respectively, just like NSF + banking. These registers are mirrored across the + entire $5000-$5FFF region (the register is selected + by the low 3 bits), but it is recommended to use the + original addresses. The mirroring is merely a + convenience for the hardware implementation. + + The 8kb CHR region may be RAM or ROM. This project + uses CHR-RAM, and the board used by infiniteneslives + for this project may only support CHR-RAM. + + At power-on, the mapper automatically sets all bits + in the $5FFF bank register, placing the highest bank + in $F000. This occurs on power-on but not on reset, + so any bank that is mapped to $F000 after power-on + should contain a valid reset vector. + + At present, the project uses iNES mapper 30 to + designate this mapper. No mapper number has been + officially reserved yet. + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_2a03pur_device::write_l) +{ + LOG_MMC(("2a03 puritans write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + if (offset >= 0x1000) + m_reg[offset & 7] = data; +} + +READ8_MEMBER(nes_2a03pur_device::read_h) +{ + LOG_MMC(("2a03 puritans read_h, offset: %04x\n", offset)); + + return m_prg[(m_reg[(offset >> 12) & 7] * 0x1000) + (offset & 0x0fff)]; +} diff --git a/src/devices/bus/nes/2a03pur.h b/src/devices/bus/nes/2a03pur.h new file mode 100644 index 00000000000..c0d80f67bd1 --- /dev/null +++ b/src/devices/bus/nes/2a03pur.h @@ -0,0 +1,33 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_2A03PUR_H +#define __NES_2A03PUR_H + +#include "nxrom.h" + + +// ======================> nes_racermate_device + +class nes_2a03pur_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_2a03pur_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[8]; +}; + + + +// device type definition +extern const device_type NES_2A03PURITANS; + +#endif diff --git a/src/devices/bus/nes/act53.c b/src/devices/bus/nes/act53.c new file mode 100644 index 00000000000..a09244b3a85 --- /dev/null +++ b/src/devices/bus/nes/act53.c @@ -0,0 +1,206 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Action 53 + + + Here we emulate the Multi-Discrete PCB designed by Tepples for + this homebew multicart [mapper 28] + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "act53.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_ACTION53 = &device_creator; + + +nes_action53_device::nes_action53_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_ACTION53, "NES Cart Action 53 PCB", tag, owner, clock, "nes_action53", __FILE__) +{ +} + + + +void nes_action53_device::device_start() +{ + common_start(); + save_item(NAME(m_sel)); + save_item(NAME(m_reg)); + m_reg[0] = 0x00; + m_reg[1] = 0x0f; + m_reg[2] = 0x00; + m_reg[3] = 0x3f; +} + +void nes_action53_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + // register content is not touched by reset + update_prg(); +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Board ACTION 53 + + In MESS: *VERY* preliminary support. + + This board uses 4 registers (reg is selected by writes to 0x5xxx) + Info from nesdev wiki + + R:$00: [...M ..CC] + C = CHR Reg + M = Mirroring + This bit overwrites bit 0 of R:$80, but only if bit 1 of + R:$80 is clear + + R:$01: [...M PPPP] + P = PRG Reg + M = Mirroring + This bit overwrites bit 0 of R:$80, but only if bit 1 of + R:$80 is clear + + R:$80: [..GG PSMM] + G = Game Size (0=32K, 1=64K, 2=128K, 3=256K) + P = PRG Size (0=32k mode, 1=16k mode) + S = Slot select: + 0 = $C000 swappable, $8000 fixed to bottom of 32K outer bank + 1 = $8000 swappable, $C000 fixed to top of 32K outer bank + This bit is ignored when 'P' is clear (32k mode) + M = Mirroring control: + %00 = 1ScA + %01 = 1ScB + %10 = Vert + %11 = Horz + + R:$81: [..BB BBBB] + Outer PRG Reg + + + -------------------------------------------------*/ + +void nes_action53_device::update_prg() +{ + UINT8 prg_lo = 0, prg_hi = 0, helper = 0; + UINT8 out = (m_reg[3] & 0x3f) << 1; // Outer PRG reg + UINT8 size = (m_reg[2] & 0x30) >> 4; // Game size + UINT8 mask = (1 << (size + 1)) - 1; // Bits to be taken from PRG reg + + if (!BIT(m_reg[2], 3)) + { + helper = (out & ~mask) | ((m_reg[1] << 1) & mask); + //32K mode + prg_lo = (helper & 0xfe); + prg_hi = (helper | 0x01); + } + else + { + helper = (out & ~mask) | (m_reg[1] & mask); + if (BIT(m_reg[2], 2)) + { + //16K mode with fixed HI + prg_lo = helper; + prg_hi = (out | 0x01); + } + else + { + //16K mode with fixed LO + prg_lo = (out & 0xfe); + prg_hi = helper; + } + } + +// printf("banks : 0x%2X - 0x%2X\n", prg_lo, prg_hi); + prg16_89ab(prg_lo); + prg16_cdef(prg_hi); +} + +void nes_action53_device::update_mirr() +{ + switch (m_reg[2] & 0x03) + { + case 0: + set_nt_mirroring(PPU_MIRROR_LOW); + break; + case 1: + set_nt_mirroring(PPU_MIRROR_HIGH); + break; + case 2: + set_nt_mirroring(PPU_MIRROR_VERT); + break; + case 3: + set_nt_mirroring(PPU_MIRROR_HORZ); + break; + } +} + +WRITE8_MEMBER(nes_action53_device::write_l) +{ + LOG_MMC(("action 53 write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + if (offset >= 0x1000) + m_sel = BIT(data, 0) | (BIT(data, 7) << 1); +} + + +WRITE8_MEMBER(nes_action53_device::write_h) +{ + LOG_MMC(("action 53 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (m_reg[m_sel] != data) + { + m_reg[m_sel] = data; + + switch (m_sel) + { + case 0: + if (!BIT(m_reg[2],1)) + { + m_reg[2] &= 0xfe; + m_reg[2] |= BIT(data,4); + update_mirr(); + } + chr8(m_reg[0] & 0x03, m_chr_source); + break; + case 1: + if (!BIT(m_reg[2],1)) + { + m_reg[2] &= 0xfe; + m_reg[2] |= BIT(data,4); + update_mirr(); + } + update_prg(); + break; + case 2: + update_prg(); + update_mirr(); + break; + case 3: + update_prg(); + break; + } + } +} diff --git a/src/devices/bus/nes/act53.h b/src/devices/bus/nes/act53.h new file mode 100644 index 00000000000..c870d600102 --- /dev/null +++ b/src/devices/bus/nes/act53.h @@ -0,0 +1,38 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_ACTION53_H +#define __NES_ACTION53_H + +#include "nxrom.h" + + +// ======================> nes_racermate_device + +class nes_action53_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_action53_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + void update_prg(); + void update_mirr(); + UINT8 m_sel; + UINT8 m_reg[4]; +}; + + + + + +// device type definition +extern const device_type NES_ACTION53; + +#endif diff --git a/src/devices/bus/nes/aladdin.c b/src/devices/bus/nes/aladdin.c new file mode 100644 index 00000000000..67749cbe12d --- /dev/null +++ b/src/devices/bus/nes/aladdin.c @@ -0,0 +1,339 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Camerica/Codemasters Aladdin Deck Enhancer pass-through cart + + + Here we emulate the following PCBs + + * Camerica ALGNV11 [mapper 71] + * Camerica ALGQV11 [mapper 232] + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "aladdin.h" + + +#ifdef NES_PCB_DEBUG +#define VERBOSE 1 +#else +#define VERBOSE 0 +#endif + +#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0) + + + +//---------------------------------- +// +// Aladdin Cartslot implementation +// +//---------------------------------- + +//------------------------------------------------- +// sub-cart interface +//------------------------------------------------- + +aladdin_cart_interface::aladdin_cart_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig, device), + m_rom(NULL), + m_rom_size(0), + m_rom_mask(0xff) +{ +} + +aladdin_cart_interface::~aladdin_cart_interface() +{ +} + +READ8_MEMBER(aladdin_cart_interface::read) +{ + if (offset < 0x4000) + return m_rom[(m_lobank * 0x4000) + (offset & 0x3fff)]; + else + return m_rom[(m_hibank * 0x4000) + (offset & 0x3fff)]; +} + +//------------------------------------------------- +// sub-cart slot device +//------------------------------------------------- + +const device_type NES_ALADDIN_SLOT = &device_creator; + +nes_aladdin_slot_device::nes_aladdin_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, NES_ALADDIN_SLOT, "NES Aladdin Deck Enhancer Cartridge Slot", tag, owner, clock, "nes_ade_slot", __FILE__), + device_image_interface(mconfig, *this), + device_slot_interface(mconfig, *this) +{ +} + +nes_aladdin_slot_device::~nes_aladdin_slot_device() +{ +} + + +void nes_aladdin_slot_device::device_start() +{ + m_cart = dynamic_cast(get_card_device()); +} + +READ8_MEMBER(nes_aladdin_slot_device::read) +{ + if (m_cart) + return m_cart->read(space, offset, mem_mask); + + return 0xff; +} + +// 128K for Dizzy The Adventurer, 256K for the others +bool nes_aladdin_slot_device::call_load() +{ + if (m_cart) + { + UINT8 *ROM = m_cart->get_cart_base(); + UINT32 size = 0; + + if (!ROM) + return IMAGE_INIT_FAIL; + + if (software_entry() == NULL) + { + if (length() != 0x20010 && length() != 0x40010) + return IMAGE_INIT_FAIL; + + UINT8 temp[0x40010]; + size = length() - 0x10; + fread(&temp, length()); + memcpy(ROM, temp + 0x10, size); + + // double check that iNES files are really mapper 71 or 232 + { + UINT8 mapper = (temp[6] & 0xf0) >> 4; + mapper |= temp[7] & 0xf0; + if (mapper != 71 && mapper != 232) + return IMAGE_INIT_FAIL; + } + } + else + { + if (get_software_region_length("rom") != 0x20000 && get_software_region_length("rom") != 0x40000) + return IMAGE_INIT_FAIL; + + size = get_software_region_length("rom"); + memcpy(ROM, get_software_region("rom"), size); + } + + m_cart->set_cart_size(size); + } + + return IMAGE_INIT_PASS; +} + + +bool nes_aladdin_slot_device::call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry) +{ + load_software_part_region(*this, swlist, swname, start_entry ); + return TRUE; +} + +void nes_aladdin_slot_device::get_default_card_software(std::string &result) +{ + if (open_image_file(mconfig().options())) + { + const char *slot_string = "algn"; + UINT32 len = core_fsize(m_file); + dynamic_buffer rom(len); + UINT8 mapper; + + core_fread(m_file, &rom[0], len); + + mapper = (rom[6] & 0xf0) >> 4; + mapper |= rom[7] & 0xf0; + +// if (mapper == 71) +// slot_string = "algn"; + if (mapper == 232) + slot_string = "algq"; + + clear(); + + result.assign(slot_string); + } + else + software_get_default_slot(result, "algn"); +} + + +//---------------------------------- +// +// Aladdin Minicart implementation +// +//---------------------------------- + +ROM_START( ade_rom ) + ROM_REGION(0x40000, "aderom", ROMREGION_ERASEFF) +ROM_END + +const device_type NES_ALGN_ROM = &device_creator; +const device_type NES_ALGQ_ROM = &device_creator; + +nes_algn_rom_device::nes_algn_rom_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) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + aladdin_cart_interface( mconfig, *this ) +{ +} + +nes_algn_rom_device::nes_algn_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, NES_ALGN_ROM, "NES Aladdin Deck Enhancer ALGN ROM", tag, owner, clock, "nes_algn_rom", __FILE__), + aladdin_cart_interface( mconfig, *this ) +{ +} + +nes_algq_rom_device::nes_algq_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_algn_rom_device(mconfig, NES_ALGQ_ROM, "NES Aladdin Deck Enhancer ALGQ ROM", tag, owner, clock, "nes_algq_rom", __FILE__) +{ +} + +void nes_algn_rom_device::device_start() +{ + m_rom = (UINT8*)memregion("aderom")->base(); + save_item(NAME(m_lobank)); +} + +void nes_algn_rom_device::device_reset() +{ + m_lobank = 0; + m_hibank = 0x0f & m_rom_mask; +} + +void nes_algq_rom_device::device_start() +{ + m_rom = (UINT8*)memregion("aderom")->base(); + save_item(NAME(m_lobank)); + save_item(NAME(m_hibank)); + save_item(NAME(m_bank_base)); +} + +void nes_algq_rom_device::device_reset() +{ + m_lobank = 0; + m_hibank = 3; + m_bank_base = 0; +} + +const rom_entry *nes_algn_rom_device::device_rom_region() const +{ + return ROM_NAME( ade_rom ); +} + +UINT8 *nes_algn_rom_device::get_cart_base() +{ + return m_rom; +} + +void nes_algn_rom_device::write_prg(UINT32 offset, UINT8 data) +{ + // m_hibank is fixed to the last available bank! + if (offset >= 0x4000) + m_lobank = data & m_rom_mask; +} + +void nes_algq_rom_device::write_prg(UINT32 offset, UINT8 data) +{ + // here hibank & lobank variables are used differently + // m_bank_base = 64K block + // m_lobank = 16K page inside the block + // m_hibank = 3rd page inside the block + if (offset < 0x4000) + { + m_bank_base = ((data >> 3) & 3) << 2; + m_lobank = m_bank_base | (m_lobank & 3); + m_hibank = m_bank_base | 3; + } + else + m_lobank = m_bank_base | (data & 3); +} + + +//----------------------------------------------- +// +// Camerica/Codemasters Aladdin passthru +// implementation +// +//----------------------------------------------- + +const device_type NES_ALADDIN = &device_creator; + +nes_aladdin_device::nes_aladdin_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_ALADDIN, "NES Cart Camerica Aladdin PCB", tag, owner, clock, "nes_aladdin", __FILE__), + m_subslot(*this, "ade_slot") +{ +} + + +void nes_aladdin_device::device_start() +{ + common_start(); +} + +void nes_aladdin_device::pcb_reset() +{ + prg32(0xff); + chr8(0, CHRRAM); +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Camerica/Codemasters Aladdin Deck Enhancer + + iNES: mapper 71 & 232 + + In MESS: Supported (but timing issues in some games) + + -------------------------------------------------*/ + +READ8_MEMBER(nes_aladdin_device::read_h) +{ + LOG_MMC(("aladdin read_h, offset: %04x\n", offset)); + // this shall be the proper code, but it's a bit slower, so we access directly the subcart below + //return m_subslot->read(space, offset, mem_mask); + + if (m_subslot->m_cart) + return m_subslot->m_cart->read(space, offset, mem_mask); + else // this is "fake" in the sense that we fill CPU space with 0xff if no Aladdin cart is loaded + return hi_access_rom(offset); +} + +WRITE8_MEMBER(nes_aladdin_device::write_h) +{ + LOG_MMC(("aladdin write_h, offset: %04x, data: %02x\n", offset, data)); + m_subslot->write_prg(offset, data); +} + +//------------------------------------------------- +// CART SLOT +//------------------------------------------------- + +static SLOT_INTERFACE_START(ade_cart) + SLOT_INTERFACE_INTERNAL("algn", NES_ALGN_ROM) + SLOT_INTERFACE_INTERNAL("algq", NES_ALGQ_ROM) +SLOT_INTERFACE_END + + +MACHINE_CONFIG_FRAGMENT( camerica_aladdin ) + MCFG_ALADDIN_MINICART_ADD("ade_slot", ade_cart) +MACHINE_CONFIG_END + +machine_config_constructor nes_aladdin_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( camerica_aladdin ); +} diff --git a/src/devices/bus/nes/aladdin.h b/src/devices/bus/nes/aladdin.h new file mode 100644 index 00000000000..18bcf988a1b --- /dev/null +++ b/src/devices/bus/nes/aladdin.h @@ -0,0 +1,167 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_ALADDIN_H +#define __NES_ALADDIN_H + +#include "nxrom.h" + + +//---------------------------------- +// +// Aladdin Cartslot implementation +// +//---------------------------------- + +// ======================> aladdin_cart_interface + +class aladdin_cart_interface : public device_slot_card_interface +{ +public: + // construction/destruction + aladdin_cart_interface(const machine_config &mconfig, device_t &device); + virtual ~aladdin_cart_interface(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read); + + UINT8 *get_cart_base() { return m_rom; } + void set_cart_size(UINT32 size) { m_rom_size = size; m_rom_mask = (size / 0x4000) - 1; } + virtual void write_prg(UINT32 offset, UINT8 data) { } + +protected: + // internal state + UINT8 *m_rom; + UINT32 m_rom_size; + UINT8 m_lobank, m_hibank, m_rom_mask; +}; + +// ======================> nes_aladdin_slot_device + +class nes_aladdin_slot_device : public device_t, + public device_image_interface, + public device_slot_interface +{ +public: + // construction/destruction + nes_aladdin_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual ~nes_aladdin_slot_device(); + + // device-level overrides + virtual void device_start(); + virtual void device_config_complete() { update_names(); } + + // image-level overrides + virtual bool call_load(); + virtual bool call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry); + + virtual iodevice_t image_type() const { return IO_CARTSLOT; } + virtual bool is_readable() const { return 1; } + virtual bool is_writeable() const { return 0; } + virtual bool is_creatable() const { return 0; } + virtual bool must_be_loaded() const { return 0; } + virtual bool is_reset_on_load() const { return 1; } + virtual const char *image_interface() const { return "ade_cart"; } + virtual const char *file_extensions() const { return "nes,bin"; } + virtual const option_guide *create_option_guide() const { return NULL; } + + // slot interface overrides + virtual void get_default_card_software(std::string &result); + + virtual DECLARE_READ8_MEMBER(read); + void write_prg(UINT32 offset, UINT8 data) { if (m_cart) m_cart->write_prg(offset, data); } + + aladdin_cart_interface* m_cart; +}; + +// device type definition +extern const device_type NES_ALADDIN_SLOT; + + +#define MCFG_ALADDIN_MINICART_ADD(_tag, _slot_intf) \ + MCFG_DEVICE_ADD(_tag, NES_ALADDIN_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, NULL, false) + + +//---------------------------------- +// +// Aladdin Minicart implementation +// +//---------------------------------- + +// ======================> nes_algn_rom_device + +class nes_algn_rom_device : public device_t, + public aladdin_cart_interface +{ +public: + // construction/destruction + nes_algn_rom_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_algn_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // optional information overrides + virtual const rom_entry *device_rom_region() const; + virtual UINT8* get_cart_base(); + virtual void write_prg(UINT32 offset, UINT8 data); + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); +}; + + +// ======================> nes_algq_rom_device + +class nes_algq_rom_device : public nes_algn_rom_device +{ +public: + // construction/destruction + nes_algq_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // optional information overrides + virtual void write_prg(UINT32 offset, UINT8 data); + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + UINT8 m_bank_base; +}; + +// device type definition +extern const device_type NES_ALGN_ROM; +extern const device_type NES_ALGQ_ROM; + + +//----------------------------------------------- +// +// Codemasters Aladdin passthru implementation +// +//----------------------------------------------- + +// ======================> nes_aladdin_device + +class nes_aladdin_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_aladdin_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + required_device m_subslot; +}; + + +// device type definition +extern const device_type NES_ALADDIN; + +#endif diff --git a/src/devices/bus/nes/ave.c b/src/devices/bus/nes/ave.c new file mode 100644 index 00000000000..bc24f1294a5 --- /dev/null +++ b/src/devices/bus/nes/ave.c @@ -0,0 +1,214 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for AVE PCBs + + + Here we emulate the following PCBs + + * AVE Nina-001 [mapper 34] + * AVE Nina-006/Nina-003/MB-91 [mapper 79] + * AVE Maxi 15 [mapper 234] + + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "ave.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_NINA001 = &device_creator; +const device_type NES_NINA006 = &device_creator; +const device_type NES_MAXI15 = &device_creator; + + +nes_nina001_device::nes_nina001_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NINA001, "NES Cart AVE Nina-001 PCB", tag, owner, clock, "nes_nina001", __FILE__) +{ +} + +nes_nina006_device::nes_nina006_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NINA006, "NES Cart AVE Nina-006 PCB", tag, owner, clock, "nes_nina006", __FILE__) +{ +} + +nes_maxi15_device::nes_maxi15_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_MAXI15, "NES Cart AVE Maxi 15 PCB", tag, owner, clock, "nes_maxi15", __FILE__) +{ +} + + + + +void nes_nina001_device::device_start() +{ + common_start(); +} + +void nes_nina001_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_nina006_device::device_start() +{ + common_start(); +} + +void nes_nina006_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + + +void nes_maxi15_device::device_start() +{ + common_start(); + save_item(NAME(m_bank)); + save_item(NAME(m_reg)); +} + +void nes_maxi15_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + set_nt_mirroring(PPU_MIRROR_VERT); + + m_reg = 0; + m_bank = 0; +} + + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + AVE NINA-001 board emulation + + iNES: mapper 34 + + Notice that in this board the bankswitch regs + overlaps WRAM, so that writes to the regs are + then readable back in WRAM (WRAM is tested by + Impossible Mission II at start) + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_nina001_device::write_m) +{ + LOG_MMC(("nina-001 write_m, offset: %04x, data: %02x\n", offset, data)); + + switch (offset) + { + case 0x1ffd: + prg32(data); + break; + case 0x1ffe: + chr4_0(data, CHRROM); + break; + case 0x1fff: + chr4_4(data, CHRROM); + break; + } + + m_prgram[offset] = data; +} + +/*------------------------------------------------- + + AVE NINA-003, NINA-006 and MB-91 boards emulation + + Games: Krazy Kreatures, Poke Block, Puzzle, Pyramid, + Solitaire, Ultimate League Soccer + + iNES: mapper 79 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_nina006_device::write_l) +{ + LOG_MMC(("nina-006 write_l, offset: %04x, data: %02x\n", offset, data)); + + if (!(offset & 0x0100)) + { + prg32(data >> 3); + chr8(data & 7, CHRROM); + } +} + +/*------------------------------------------------- + + AVE Maxi 15 boards emulation + + Games: Maxi 15 + + iNES: mapper 234 + + In MESS: Partially Supported. + + -------------------------------------------------*/ + +void nes_maxi15_device::update_banks() +{ + if (m_bank & 0x40) + { + prg32((m_bank & 0x0e) | (m_reg & 1)); + chr8(((m_bank & 0x0e) << 2) | ((m_reg >> 4) & 7), m_chr_source); + } + else + { + prg32(m_bank & 0x0f); + chr8(((m_bank & 0x0f) << 2) | ((m_reg >> 4) & 3), m_chr_source); + } +} + +READ8_MEMBER(nes_maxi15_device::read_h) +{ + LOG_MMC(("Maxi 15 read_h, offset: %04x\n", offset)); + + if (offset >= 0x7f80 && offset < 0x7fa0) + { + m_bank = hi_access_rom(offset); + set_nt_mirroring(BIT(m_bank, 7) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + update_banks(); + return m_bank; + } + if (offset >= 0x7fe8 && offset < 0x7ff8) + { + m_reg = hi_access_rom(offset); + update_banks(); + return m_reg; + } + + return hi_access_rom(offset); +} diff --git a/src/devices/bus/nes/ave.h b/src/devices/bus/nes/ave.h new file mode 100644 index 00000000000..9d35c3eb623 --- /dev/null +++ b/src/devices/bus/nes/ave.h @@ -0,0 +1,69 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_AVE_H +#define __NES_AVE_H + +#include "nxrom.h" + + +// ======================> nes_nina001_device + +class nes_nina001_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_nina001_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + + +// ======================> nes_nina006_device + +class nes_nina006_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_nina006_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); +}; + + +// ======================> nes_maxi15_device + +class nes_maxi15_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_maxi15_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_h); + + virtual void pcb_reset(); + +private: + void update_banks(); + UINT8 m_reg, m_bank; +}; + + + + + +// device type definition +extern const device_type NES_NINA001; +extern const device_type NES_NINA006; +extern const device_type NES_MAXI15; + +#endif diff --git a/src/devices/bus/nes/bandai.c b/src/devices/bus/nes/bandai.c new file mode 100644 index 00000000000..4aa54055969 --- /dev/null +++ b/src/devices/bus/nes/bandai.c @@ -0,0 +1,479 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Bandai PCBs + + + Here we emulate the following PCBs + + * Bandai FCG [mapper 16] (older design, regs are only accessed in 0x6000-0x7fff range) + * Bandai LZ93D50 [mapper 16] (this extends FCG by solving issues when writing to 0x8000-0xffff) + * Bandai LZ93D50 + 24C01 EEPROM [mapper 159] + * Bandai LZ93D50 + 24C02 EEPROM [mapper 16] + * Bandai Famicom Jump 2 (aka LZ93D50 + SRAM) [mapper 153] + * Bandai Oeka Kids [mapper 96] + + * Bandai Datach Joint ROM System [mapper 157] is emulated in a separate source file + to implement also the subslot, but the PCB is basically a Bandai LZ93D50 + 24C02 EEPROM + pcb with added barcode reader and subslot + + * Bandai Karaoke Studio [mapper 188] is emulated in a separate source file + to implement also the subslot and the mic inputs + + + TODO: + - investigate why EEPROM does not work + - add support to the PPU for the code necessary to Oeka Kids games (also needed by UNL-DANCE2000 PCB) + - check the cause for the flickering in Famicom Jump 2 + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "bandai.h" + +#include "cpu/m6502/m6502.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_OEKAKIDS = &device_creator; +const device_type NES_FCG = &device_creator; +const device_type NES_LZ93D50 = &device_creator; +const device_type NES_LZ93D50_24C01 = &device_creator; +const device_type NES_LZ93D50_24C02 = &device_creator; +const device_type NES_FJUMP2 = &device_creator; + + +nes_oekakids_device::nes_oekakids_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_OEKAKIDS, "NES Cart Bandai Oeka Kids PCB", tag, owner, clock, "nes_oeka", __FILE__) +{ +} + +nes_fcg_device::nes_fcg_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_fcg_device::nes_fcg_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_FCG, "NES Cart Bandai FCG PCB", tag, owner, clock, "nes_fcg", __FILE__) +{ +} + +nes_lz93d50_device::nes_lz93d50_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_fcg_device(mconfig, type, name, tag, owner, clock, shortname, source) +{ +} + +nes_lz93d50_device::nes_lz93d50_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_fcg_device(mconfig, NES_LZ93D50, "NES Cart Bandai LZ93D50 PCB", tag, owner, clock, "nes_lz93d50", __FILE__) +{ +} + +nes_lz93d50_24c01_device::nes_lz93d50_24c01_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_lz93d50_device(mconfig, type, name, tag, owner, clock, shortname, source), + m_i2cmem(*this, "i2cmem") +{ +} + +nes_lz93d50_24c01_device::nes_lz93d50_24c01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_lz93d50_device(mconfig, NES_LZ93D50_24C01, "NES Cart Bandai LZ93D50 + 24C01 PCB", tag, owner, clock, "nes_lz93d50_ep1", __FILE__), + m_i2cmem(*this, "i2cmem") +{ +} + +nes_lz93d50_24c02_device::nes_lz93d50_24c02_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_lz93d50_24c01_device(mconfig, NES_LZ93D50_24C02, "NES Cart Bandai LZ93D50 + 24C02 PCB", tag, owner, clock, "nes_lz93d50_ep2", __FILE__) +{ +} + +nes_fjump2_device::nes_fjump2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_lz93d50_device(mconfig, NES_FJUMP2, "NES Cart Bandai Famicom Jump II PCB", tag, owner, clock, "nes_fjump2", __FILE__) +{ +} + + + +void nes_oekakids_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + save_item(NAME(m_reg)); +} + +void nes_oekakids_device::pcb_reset() +{ + prg32(0); + chr4_0(0, CHRRAM); + chr4_4(3, CHRRAM); + set_nt_mirroring(PPU_MIRROR_LOW); + m_latch = 0; + m_reg = 0; +} + +void nes_fcg_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); +} + +void nes_fcg_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_count = 0; +} + +void nes_lz93d50_24c01_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_i2c_dir)); +} + +void nes_lz93d50_24c01_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_count = 0; + m_i2c_dir = 0; +} + +void nes_fjump2_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_reg)); +} + +void nes_fjump2_device::pcb_reset() +{ + chr8(0, CHRRAM); + memset(m_reg, 0, sizeof(m_reg)); + set_prg(); + + m_irq_enable = 0; +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Bandai Oeka Kids board emulation + + Games: Oeka Kids - Anpanman no Hiragana Daisuki, Oeka + Kids - Anpanman to Oekaki Shiyou!! + + This board can swap CHR whenever a PPU address line is + changed and we still do not emulate this. + + iNES: mapper 96 + + In MESS: Preliminary Support. + + -------------------------------------------------*/ + + +WRITE8_MEMBER(nes_oekakids_device::nt_w) +{ + int page = ((offset & 0xc00) >> 10); + +#if 0 + if (!(offset & 0x1000) && (offset & 0x3ff) < 0x3c0) + { + m_latch = (offset & 0x300) >> 8; + chr4_0(m_reg | m_latch, CHRRAM); + } +#endif + + m_nt_access[page][offset & 0x3ff] = data; +} + +READ8_MEMBER(nes_oekakids_device::nt_r) +{ + int page = ((offset & 0xc00) >> 10); + +#if 0 + if (!(offset & 0x1000) && (offset & 0x3ff) < 0x3c0) + { + m_latch = (offset & 0x300) >> 8; + chr4_0(m_reg | m_latch, CHRRAM); + } +#endif + + return m_nt_access[page][offset & 0x3ff]; +} + +void nes_oekakids_device::update_chr() +{ + chr4_0(m_reg | m_latch, CHRRAM); + chr4_4(m_reg | 0x03, CHRRAM); +} + +// this only monitors accesses to $2007 while we would need to monitor accesses to $2006... +void nes_oekakids_device::ppu_latch(offs_t offset) +{ +#if 0 + if ((offset & 0x3000) == 0x2000) + { + m_latch = (offset & 0x300) >> 8; + update_chr(); + } +#endif +} + +WRITE8_MEMBER(nes_oekakids_device::write_h) +{ + LOG_MMC(("oeka kids write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(data & 0x03); + m_reg = data & 0x04; + update_chr(); +} + +/*------------------------------------------------- + + Bandai FCG / LZ93D50 boards emulation + + There are several variants: plain board with or without SRAM, + board + 24C01 EEPROM, board + 24C02 EEPROM, board + Barcode + Reader (DATACH). + We currently only emulate the base hardware. + + Games: Crayon Shin-Chan - Ora to Poi Poi, Dragon Ball Z Gaiden, + Dragon Ball Z II & III, Rokudenashi Blues, SD Gundam + Gaiden - KGM2, Dragon Ball Z, Magical Taruruuto-kun, SD Gundam + Gaiden [with EEPROM], Dragon Ball, Dragon Ball 3, Famicom Jump, + Famicom Jump II [no EEPROM], Datach Games + + At the moment, we don't support EEPROM I/O + + iNES: mappers 16, 153 (see below), 157 & 159 + + In MESS: Supported + + -------------------------------------------------*/ + +void nes_fcg_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + // 16bit counter, IRQ fired when the counter goes from 1 to 0 + // after firing, the counter is *not* reloaded, but next clock + // counter wraps around from 0 to 0xffff + if (!m_irq_count) + m_irq_count = 0xffff; + else + m_irq_count--; + + if (!m_irq_count) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + m_irq_enable = 0; + } + } + } +} + +WRITE8_MEMBER(nes_fcg_device::fcg_write) +{ + LOG_MMC(("lz93d50_write, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x0f) + { + case 0: case 1: case 2: case 3: + case 4: case 5: case 6: case 7: + chr1_x(offset & 0x07, data, m_chr_source); + break; + case 8: + prg16_89ab(data); + break; + case 9: + switch (data & 0x03) + { + case 0: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 1: set_nt_mirroring(PPU_MIRROR_HORZ); break; + case 2: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 3: set_nt_mirroring(PPU_MIRROR_HIGH); break; + } + break; + case 0x0a: + m_irq_enable = data & 0x01; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x0b: + m_irq_count = (m_irq_count & 0xff00) | data; + break; + case 0x0c: + m_irq_count = (m_irq_count & 0x00ff) | (data << 8); + break; + default: + logerror("lz93d50_write uncaught write, offset: %04x, data: %02x\n", offset, data); + break; + } +} + +WRITE8_MEMBER(nes_fcg_device::write_m) +{ + LOG_MMC(("lz93d50 write_m, offset: %04x, data: %02x\n", offset, data)); + + if (m_battery.empty() && m_prgram.empty()) + fcg_write(space, offset & 0x0f, data, mem_mask); + else if (!m_battery.empty()) + m_battery[offset] = data; + else + m_prgram[offset] = data; +} + +// FCG board does not access regs in 0x8000-0xffff space! +// only later design lz93d50 (and its variants do)! + +WRITE8_MEMBER(nes_lz93d50_24c01_device::write_h) +{ + LOG_MMC(("lz93d50_24c01 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x0f) + { + case 0x0d: + m_i2cmem->write_scl(BIT(data, 5)); + m_i2cmem->write_sda(BIT(data, 6)); + m_i2c_dir = BIT(data, 7); + break; + default: + fcg_write(space, offset & 0x0f, data, mem_mask); + break; + } +} + +READ8_MEMBER(nes_lz93d50_24c01_device::read_m) +{ + LOG_MMC(("lz93d50 EEPROM read, offset: %04x\n", offset)); + if (m_i2c_dir) + return (m_i2cmem->read_sda() & 1) << 4; + else + return 0; +} + +//------------------------------------------------- +// SERIAL I2C DEVICE +//------------------------------------------------- + +MACHINE_CONFIG_FRAGMENT( bandai_i2c_24c01 ) + MCFG_24C01_ADD("i2cmem") +MACHINE_CONFIG_END + +machine_config_constructor nes_lz93d50_24c01_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( bandai_i2c_24c01 ); +} + + +MACHINE_CONFIG_FRAGMENT( bandai_i2c_24c02 ) + MCFG_24C02_ADD("i2cmem") +MACHINE_CONFIG_END + +machine_config_constructor nes_lz93d50_24c02_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( bandai_i2c_24c02 ); +} + + +/*------------------------------------------------- + + Bandai BANDAI-JUMP2 boards emulation + + This is a variant of LZ93D50, with SRAM and no EEPROM + The board is only used by Famicom Jump II, which + has no CHR and 512K of PRG, so it is not completely + clear if the CHR regs of LZ93D50 (i.e. offset & 0xf < 8) + would switch also CHR or if they are only used to select + upper 256K of PRG + + iNES: mappers 153 + + In MESS: Supported + + -------------------------------------------------*/ + +void nes_fjump2_device::set_prg() +{ + UINT8 prg_base = 0; + + for (int i = 0; i < 4; i++) + prg_base |= (m_reg[i] << 4); + + prg16_89ab(prg_base | m_reg[4]); + prg16_cdef(prg_base | 0x0f); +} + +READ8_MEMBER(nes_fjump2_device::read_m) +{ + LOG_MMC(("fjump2 read_m, offset: %04x\n", offset)); + return m_battery[offset & (m_battery.size() - 1)]; +} + +WRITE8_MEMBER(nes_fjump2_device::write_m) +{ + LOG_MMC(("fjump2 write_m, offset: %04x, data: %02x\n", offset, data)); + m_battery[offset & (m_battery.size() - 1)] = data; +} + +WRITE8_MEMBER(nes_fjump2_device::write_h) +{ + LOG_MMC(("fjump2 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x0f) + { + case 0: case 1: case 2: case 3: + m_reg[offset & 0x0f] = BIT(data,0); + set_prg(); + break; + case 4: case 5: case 6: case 7: + // these have been verified to be disabled in this board + break; + case 8: + m_reg[4] = data & 0x0f; + set_prg(); + break; + default: + fcg_write(space, offset & 0x0f, data, mem_mask); + break; + } +} diff --git a/src/devices/bus/nes/bandai.h b/src/devices/bus/nes/bandai.h new file mode 100644 index 00000000000..ae19589be37 --- /dev/null +++ b/src/devices/bus/nes/bandai.h @@ -0,0 +1,141 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_BANDAI_H +#define __NES_BANDAI_H + +#include "nxrom.h" +#include "machine/i2cmem.h" + + +// ======================> nes_oekakids_device + +class nes_oekakids_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_oekakids_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual DECLARE_READ8_MEMBER(nt_r); + virtual DECLARE_WRITE8_MEMBER(nt_w); + + virtual void pcb_reset(); + + virtual void ppu_latch(offs_t offset); + + // TODO: add oeka kids controller emulation +protected: + void update_chr(); + UINT8 m_reg, m_latch; +}; + + +// ======================> nes_fcg_device + +class nes_fcg_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_fcg_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_fcg_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_WRITE8_MEMBER(fcg_write); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); + +protected: + UINT16 m_irq_count; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; +}; + + +// ======================> nes_lz93d50_device + +class nes_lz93d50_device : public nes_fcg_device +{ +public: + // construction/destruction + nes_lz93d50_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_lz93d50_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h) { fcg_write(space, offset, data, mem_mask); } +}; + + +// ======================> nes_lz93d50_24c01_device + +class nes_lz93d50_24c01_device : public nes_lz93d50_device +{ +public: + // construction/destruction + nes_lz93d50_24c01_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_lz93d50_24c01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + + // TODO: fix EEPROM I/O emulation + required_device m_i2cmem; + UINT8 m_i2c_dir; +}; + + +// ======================> nes_lz93d50_24c02_device + +class nes_lz93d50_24c02_device : public nes_lz93d50_24c01_device +{ +public: + // construction/destruction + nes_lz93d50_24c02_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual machine_config_constructor device_mconfig_additions() const; +}; + + +// ======================> nes_fjump2_device + +class nes_fjump2_device : public nes_lz93d50_device +{ +public: + // construction/destruction + nes_fjump2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + void set_prg(); + UINT8 m_reg[5]; +}; + + +// device type definition +extern const device_type NES_OEKAKIDS; +extern const device_type NES_FCG; +extern const device_type NES_LZ93D50; +extern const device_type NES_LZ93D50_24C01; +extern const device_type NES_LZ93D50_24C02; +extern const device_type NES_FJUMP2; + +#endif diff --git a/src/devices/bus/nes/benshieng.c b/src/devices/bus/nes/benshieng.c new file mode 100644 index 00000000000..b96c75e6297 --- /dev/null +++ b/src/devices/bus/nes/benshieng.c @@ -0,0 +1,105 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Benshieng PCBs + + + Here we emulate the following PCBs used by Benshieng multigame carts series + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "benshieng.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_BENSHIENG = &device_creator; + + +nes_benshieng_device::nes_benshieng_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BENSHIENG, "NES Cart Benshieng PCB", tag, owner, clock, "nes_benshieng", __FILE__) +{ +} + + + + +void nes_benshieng_device::device_start() +{ + common_start(); + save_item(NAME(m_dipsetting)); + save_item(NAME(m_mmc_prg_bank)); + save_item(NAME(m_mmc_vrom_bank)); +} + +void nes_benshieng_device::pcb_reset() +{ + m_dipsetting = 0; + + m_mmc_prg_bank[0] = 0xff; + m_mmc_prg_bank[1] = 0xff; + m_mmc_prg_bank[2] = 0xff; + m_mmc_prg_bank[3] = 0xff; + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); + update_banks(); +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + BMC-BS-5 + + Games: a few 4 in 1 multicarts + + -------------------------------------------------*/ + +void nes_benshieng_device::update_banks() +{ + prg8_89(m_mmc_prg_bank[0]); + prg8_ab(m_mmc_prg_bank[1]); + prg8_cd(m_mmc_prg_bank[2]); + prg8_ef(m_mmc_prg_bank[3]); + chr2_0(m_mmc_vrom_bank[0], CHRROM); + chr2_2(m_mmc_vrom_bank[1], CHRROM); + chr2_4(m_mmc_vrom_bank[2], CHRROM); + chr2_6(m_mmc_vrom_bank[3], CHRROM); +} + +WRITE8_MEMBER(nes_benshieng_device::write_h) +{ + UINT8 helper = (offset & 0xc00) >> 10; + LOG_MMC(("benshieng write_h, offset: %04x, data: %02x\n", offset, data)); +// m_mmc_dipsetting = ioport("CARTDIPS")->read(); + + switch (offset & 0x7000) + { + case 0x0000: + m_mmc_vrom_bank[helper] = offset & 0x1f; + break; + case 0x2000: + if (BIT(offset, m_dipsetting + 4)) // mmc_dipsetting is always zero atm, given we have no way to add cart-based DIPs + m_mmc_prg_bank[helper] = offset & 0x0f; + break; + } + update_banks(); +} diff --git a/src/devices/bus/nes/benshieng.h b/src/devices/bus/nes/benshieng.h new file mode 100644 index 00000000000..2a042a432e4 --- /dev/null +++ b/src/devices/bus/nes/benshieng.h @@ -0,0 +1,34 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_BENSHIENG_H +#define __NES_BENSHIENG_H + +#include "nxrom.h" + + +// ======================> nes_benshieng_device + +class nes_benshieng_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_benshieng_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + void update_banks(); + UINT8 m_dipsetting; + UINT8 m_mmc_prg_bank[4]; + UINT8 m_mmc_vrom_bank[4]; +}; + + +// device type definition +extern const device_type NES_BENSHIENG; + +#endif diff --git a/src/devices/bus/nes/bootleg.c b/src/devices/bus/nes/bootleg.c new file mode 100644 index 00000000000..23b13b51711 --- /dev/null +++ b/src/devices/bus/nes/bootleg.c @@ -0,0 +1,1635 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Bootleg PCBs + + + Here we emulate the PCBs used in FDS2NES conversions which are common in the Taiwanese & HK markets + Notice that many of these have unusual PRG sizes (32KB+8KB, 32KB+24KB) partially mapped in the WRAM + area, or WRAM overlapping the usual PRG area, so that we often skip the usual bankswitching mechanisms + in favor of direct handling of the PRG accesses + + TODO: + - review all PCBs and fix the starting banks (which are often the main problem of not working games) + - investigate pcbs listed in FCEUmm but with apparently no dumps available (LE05 and LH53) + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "bootleg.h" + +#include "cpu/m6502/m6502.h" +#include "video/ppu2c0x.h" // this has to be included so that IRQ functions can access PPU_BOTTOM_VISIBLE_SCANLINE + + +#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_AX5705 = &device_creator; +const device_type NES_SC127 = &device_creator; +const device_type NES_MARIOBABY = &device_creator; +const device_type NES_ASN = &device_creator; +const device_type NES_SMB3PIRATE = &device_creator; +const device_type NES_BTL_DNINJA = &device_creator; +const device_type NES_WHIRLWIND_2706 = &device_creator; +const device_type NES_SMB2J = &device_creator; +const device_type NES_SMB2JA = &device_creator; +const device_type NES_SMB2JB = &device_creator; +const device_type NES_09034A = &device_creator; +const device_type NES_TOBIDASE = &device_creator; +const device_type NES_LH32 = &device_creator; +const device_type NES_LH10 = &device_creator; +const device_type NES_LH53 = &device_creator; +const device_type NES_2708 = &device_creator; +const device_type NES_AC08 = &device_creator; +const device_type NES_UNL_BB = &device_creator; +const device_type NES_MMALEE = &device_creator; +const device_type NES_SHUIGUAN = &device_creator; + + +nes_ax5705_device::nes_ax5705_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_AX5705, "NES Cart AX5705 PCB", tag, owner, clock, "nes_ax5705", __FILE__) +{ +} + +nes_sc127_device::nes_sc127_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SC127, "NES Cart SC-127 PCB", tag, owner, clock, "nes_sc127", __FILE__) +{ +} + +nes_mbaby_device::nes_mbaby_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_MARIOBABY, "NES Cart Mario Baby Bootleg PCB", tag, owner, clock, "nes_mbaby", __FILE__) +{ +} + +nes_asn_device::nes_asn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_ASN, "NES Cart Ai Senshi Nicol Bootleg PCB", tag, owner, clock, "nes_asn", __FILE__) +{ +} + +nes_smb3p_device::nes_smb3p_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SMB3PIRATE, "NES Cart Super Mario Bros. 3 Pirate PCB", tag, owner, clock, "nes_smb3p", __FILE__) +{ +} + +nes_btl_dn_device::nes_btl_dn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BTL_DNINJA, "NES Cart DragonNinja Pirate PCB", tag, owner, clock, "nes_btl_dn", __FILE__) +{ +} + +nes_whirl2706_device::nes_whirl2706_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_WHIRLWIND_2706, "NES Cart Whirlwind 2706 PCB", tag, owner, clock, "nes_whirl2706", __FILE__) +{ +} + +nes_smb2j_device::nes_smb2j_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SMB2J, "NES Cart Super Mario Bros. 2 Jpn PCB", tag, owner, clock, "nes_smb2j", __FILE__) +{ +} + +nes_smb2ja_device::nes_smb2ja_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SMB2JA, "NES Cart Super Mario Bros. 2 Jpn (Alt) PCB", tag, owner, clock, "nes_smb2ja", __FILE__) +{ +} + +nes_smb2jb_device::nes_smb2jb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SMB2JB, "NES Cart Super Mario Bros. 2 Jpn (Alt 2) PCB", tag, owner, clock, "nes_smb2jb", __FILE__) +{ +} + +nes_09034a_device::nes_09034a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_09034A, "NES Cart 09-034A PCB", tag, owner, clock, "nes_09034a", __FILE__) +{ +} + +nes_tobidase_device::nes_tobidase_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_TOBIDASE, "NES Cart Tobidase Daisakusen Pirate PCB", tag, owner, clock, "nes_tobidase", __FILE__) +{ +} + +nes_lh32_device::nes_lh32_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_LH32, "NES Cart LH-32 Pirate PCB", tag, owner, clock, "nes_lh32", __FILE__) +{ +} + +nes_lh10_device::nes_lh10_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_LH10, "NES Cart LH-10 Pirate PCB", tag, owner, clock, "nes_lh10", __FILE__) +{ +} + +nes_lh53_device::nes_lh53_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_LH53, "NES Cart LH-53 Pirate PCB", tag, owner, clock, "nes_lh53", __FILE__) +{ +} + +nes_2708_device::nes_2708_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_2708, "NES Cart BTL-2708 Pirate PCB", tag, owner, clock, "nes_2708", __FILE__) +{ +} + +nes_ac08_device::nes_ac08_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_AC08, "NES Cart AC08 Pirate PCB", tag, owner, clock, "nes_ac08", __FILE__) +{ +} + +nes_unl_bb_device::nes_unl_bb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_UNL_BB, "NES Cart FDS+CHR Pirate PCB", tag, owner, clock, "nes_unl_bb", __FILE__) +{ +} + +nes_mmalee_device::nes_mmalee_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_MMALEE, "NES Cart Super Mario Bros. Malee 2 Pirate PCB", tag, owner, clock, "nes_mmalee", __FILE__) +{ +} + +nes_shuiguan_device::nes_shuiguan_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SHUIGUAN, "NES Cart Shui Guan Pipe Pirate PCB", tag, owner, clock, "nes_shuiguan", __FILE__) +{ +} + + + + +void nes_ax5705_device::device_start() +{ + common_start(); + save_item(NAME(m_mmc_prg_bank)); + save_item(NAME(m_mmc_vrom_bank)); +} + +void nes_ax5705_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + chr8(0, m_chr_source); + + m_mmc_prg_bank[0] = 0; + m_mmc_prg_bank[1] = 1; + prg8_89(m_mmc_prg_bank[0]); + prg8_ab(m_mmc_prg_bank[1]); + prg8_cd(0xfe); + prg8_ef(0xff); + + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); +} + +void nes_sc127_device::device_start() +{ + common_start(); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); +} + +void nes_sc127_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0xff); + chr8(0, m_chr_source); + + m_irq_enable = 0; + m_irq_count = 0; +} + +void nes_mbaby_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->reset(); + timer_freq = machine().device("maincpu")->cycles_to_attotime(24576); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_latch)); +} + +void nes_mbaby_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32((m_prg_chunks - 1) >> 1); + chr8(0, m_chr_source); + + m_irq_enable = 0; + m_latch = 0; +} + +void nes_asn_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_asn_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32((m_prg_chunks - 1) >> 1); + chr8(0, m_chr_source); + + m_latch = 0; +} + +void nes_smb3p_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); +} + +void nes_smb3p_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg8_89((m_prg_chunks << 1) - 1); + prg8_ab(0); + prg8_cd(0); + prg8_ef((m_prg_chunks << 1) - 1); + chr8(0, m_chr_source); + + m_irq_enable = 0; + m_irq_count = 0; +} + +void nes_btl_dn_device::device_start() +{ + common_start(); + save_item(NAME(m_irq_count)); +} + +void nes_btl_dn_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_count = 0; +} + +void nes_whirl2706_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_whirl2706_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0xff); + chr8(0, m_chr_source); + + m_latch = 0; +} + +void nes_smb2j_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); +} + +void nes_smb2j_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + chr8(0, m_chr_source); + prg8_89(1); + prg8_ab(0); + prg8_cd(0); + prg8_ef(9); + + m_irq_enable = 0; + m_irq_count = 0; +} + +void nes_smb2ja_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); +} + +void nes_smb2ja_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg8_89(0xfc); + prg8_ab(0xfd); + prg8_cd(0xfe); + prg8_ef(0xff); + chr8(0, m_chr_source); + + m_irq_enable = 0; + m_irq_count = 0; +} + +void nes_smb2jb_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); +} + +void nes_smb2jb_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg8_89(0x08); + prg8_ab(0x09); + prg8_cd(0); + prg8_ef(0x0b); + chr8(0, m_chr_source); + + m_irq_enable = 0; + m_irq_count = 0; +} + +void nes_09034a_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_09034a_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + m_reg = 0; +} + +void nes_tobidase_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_tobidase_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(2); + chr8(0, m_chr_source); + + m_latch = 0; +} + +void nes_lh32_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_lh32_device::pcb_reset() +{ + chr8(0, CHRRAM); + + prg32((m_prg_chunks - 1) >> 1); + // 0xc000-0xdfff reads/writes WRAM + m_latch = 0xf; +} + +void nes_lh10_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + save_item(NAME(m_reg)); +} + +void nes_lh10_device::pcb_reset() +{ + chr8(0, CHRRAM); + + prg8_89(0); + prg8_ab(0); + // 0xc000-0xdfff reads/writes WRAM + prg8_ef(0xff); + memset(m_reg, 0, sizeof(m_reg)); + m_latch = 0; +} + +void nes_lh53_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_reg)); +} + +void nes_lh53_device::pcb_reset() +{ + chr8(0, CHRRAM); + + prg8_89(0xc); + prg8_ab(0xd); // last 2K are overlayed by WRAM + prg8_cd(0xe); // first 6K are overlayed by WRAM + prg8_ef(0xf); + m_reg = 0; + m_irq_count = 0; + m_irq_enable = 0; +} + +void nes_2708_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_2708_device::pcb_reset() +{ + chr8(0, CHRRAM); + + prg32(7); + // the upper PRG banks never change, but there are 8K of WRAM overlayed to the ROM area based on reg1 + m_reg[0] = 0; + m_reg[1] = 0; +} + +void nes_ac08_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_ac08_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + chr8(0, m_chr_source); + prg32(0xff); + m_latch = 0xff; +} + +void nes_unl_bb_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_unl_bb_device::pcb_reset() +{ + chr8(0, CHRROM); + prg32(0xff); + // the upper PRG banks never change, but there are 8K of WRAM overlayed to the ROM area based on reg1 + m_reg[0] = 0xff; + m_reg[1] = 0; +} + +void nes_mmalee_device::device_start() +{ + common_start(); +} + +void nes_mmalee_device::pcb_reset() +{ + chr8(0, CHRROM); + prg32(0); +} + +void nes_shuiguan_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + // always running and checking for IRQ every 114 cycles? or resetting every frame? + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(114)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_mmc_vrom_bank)); +} + +void nes_shuiguan_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32((m_prg_chunks << 1) - 1); + chr8(0, m_chr_source); + + m_irq_enable = 0; + m_irq_count = 0; + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Board UNL-AX5705 + + Games: Super Mario Bros. Pocker Mali (Crayon Shin-chan pirate hack) + + In MESS: Supported + + -------------------------------------------------*/ + +void nes_ax5705_device::set_prg() +{ + prg8_89(m_mmc_prg_bank[0]); + prg8_ab(m_mmc_prg_bank[1]); +} + +WRITE8_MEMBER(nes_ax5705_device::write_h) +{ + UINT8 bank; + LOG_MMC(("ax5705 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x700f) + { + case 0x0000: + m_mmc_prg_bank[0] = (data & 0x05) | ((data & 0x08) >> 2) | ((data & 0x02) << 2); + set_prg(); + break; + case 0x0008: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 0x2000: + m_mmc_prg_bank[1] = (data & 0x05) | ((data & 0x08) >> 2) | ((data & 0x02) << 2); + set_prg(); + break; + /* CHR banks 0, 1, 4, 5 */ + case 0x2008: + case 0x200a: + case 0x4008: + case 0x400a: + bank = ((offset & 0x4000) ? 4 : 0) + ((offset & 0x0002) ? 1 : 0); + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | (data & 0x0f); + chr1_x(bank, m_mmc_vrom_bank[bank], CHRROM); + break; + case 0x2009: + case 0x200b: + case 0x4009: + case 0x400b: + bank = ((offset & 0x4000) ? 4 : 0) + ((offset & 0x0002) ? 1 : 0); + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x04) << 3) | ((data & 0x02) << 5) | ((data & 0x09) << 4); + chr1_x(bank, m_mmc_vrom_bank[bank], CHRROM); + break; + /* CHR banks 2, 3, 6, 7 */ + case 0x4000: + case 0x4002: + case 0x6000: + case 0x6002: + bank = 2 + ((offset & 0x2000) ? 4 : 0) + ((offset & 0x0002) ? 1 : 0); + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | (data & 0x0f); + chr1_x(bank, m_mmc_vrom_bank[bank], CHRROM); + break; + case 0x4001: + case 0x4003: + case 0x6001: + case 0x6003: + bank = 2 + ((offset & 0x2000) ? 4 : 0) + ((offset & 0x0002) ? 1 : 0); + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x04) << 3) | ((data & 0x02) << 5) | ((data & 0x09) << 4); + chr1_x(bank, m_mmc_vrom_bank[bank], CHRROM); + break; + } +} + +/*------------------------------------------------- + + SC-127 Board + + Games: Wario World II (Kirby Hack) + + iNES: mapper 35 + + In MESS: Supported + + -------------------------------------------------*/ + +void nes_sc127_device::hblank_irq(int scanline, int vblank, int blanked) +{ + if (scanline < PPU_BOTTOM_VISIBLE_SCANLINE && m_irq_enable) + { + m_irq_count--; + + if (!blanked && (m_irq_count == 0)) + { + LOG_MMC(("irq fired, scanline: %d (MAME %d, beam pos: %d)\n", scanline, + machine().first_screen()->vpos(), machine().first_screen()->hpos())); + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + m_irq_enable = 0; + } + } +} + +WRITE8_MEMBER(nes_sc127_device::write_h) +{ + LOG_MMC(("sc127 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset) + { + case 0x0000: + prg8_89(data); + break; + case 0x0001: + prg8_ab(data); + break; + case 0x0002: +// m_mmc_prg_bank[offset & 0x02] = data; + prg8_cd(data); + break; + case 0x1000: + case 0x1001: + case 0x1002: + case 0x1003: + case 0x1004: + case 0x1005: + case 0x1006: + case 0x1007: +// m_mmc_vrom_bank[offset & 0x07] = data; + chr1_x(offset & 0x07, data, CHRROM); + break; + case 0x4002: + m_irq_enable = 0; + break; + case 0x4003: + m_irq_enable = 1; + break; + case 0x4005: + m_irq_count = data; + break; + case 0x5001: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + } +} + +/*------------------------------------------------- + + BTL-MARIOBABY + + Games: Mario Baby, Ai Senshi Nicol + + iNES: mapper 42 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_mbaby_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + irq_timer->adjust(attotime::never); + } +} + +WRITE8_MEMBER(nes_mbaby_device::write_h) +{ + LOG_MMC(("Mario Baby write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset >= 0x7000) + { + switch (offset & 0x03) + { + case 0x00: + m_latch = data; + break; + case 0x01: + set_nt_mirroring(BIT(data, 3) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 0x02: + /* Check if IRQ is being enabled */ + if (!m_irq_enable && (data & 0x02)) + { + m_irq_enable = 1; + irq_timer->adjust(timer_freq); + } + if (!(data & 0x02)) + { + m_irq_enable = 0; + irq_timer->adjust(attotime::never); + } + break; + } + } +} + +READ8_MEMBER(nes_mbaby_device::read_m) +{ + LOG_MMC(("Mario Baby read_m, offset: %04x\n", offset)); + return m_prg[(m_latch * 0x2000) + (offset & 0x1fff)]; +} + +/*------------------------------------------------- + + BTL-AISENSHINICOL + + Games: Ai Senshi Nicol + + iNES: mapper 42 with no IRQ and no NT, but CHR switch + + In MESS: Partially Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_asn_device::write_h) +{ + LOG_MMC(("Ai Senshi Nicol write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset == 0x0000) + chr8(data, CHRROM); + + if (offset == 0x7000) + m_latch = data; +} + +READ8_MEMBER(nes_asn_device::read_m) +{ + LOG_MMC(("Ai Senshi Nicol read_m, offset: %04x\n", offset)); + return m_prg[((m_latch * 0x2000) + (offset & 0x1fff)) & (m_prg_size - 1)]; +} + + +/*------------------------------------------------- + + BTL-SMB3 + + Games: Super Mario Bros. 3 Pirate + + iNES: mapper 106 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_smb3p_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (m_irq_count == 0xffff) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + m_irq_enable = 0; + } + else + m_irq_count++; + } + } +} + +WRITE8_MEMBER(nes_smb3p_device::write_h) +{ + LOG_MMC(("btl_smb3_w, offset: %04x, data: %02x\n", offset, data)); + switch (offset & 0x0f) + { + case 0x00: + case 0x02: + chr1_x(offset & 0x07, data & 0xfe, CHRROM); + break; + case 0x01: + case 0x03: + chr1_x(offset & 0x07, data | 0x01, CHRROM); + break; + case 0x04: case 0x05: + case 0x06: case 0x07: + chr1_x(offset & 0x07, data, CHRROM); + break; + case 0x08: + prg8_89(data | 0x10); + break; + case 0x09: + prg8_ab(data); + break; + case 0x0a: + prg8_cd(data); + break; + case 0x0b: + prg8_ef(data | 0x10); + break; + case 0x0c: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 0x0d: + m_irq_count = 0; + m_irq_enable = 0; + break; + case 0x0e: + m_irq_count = (m_irq_count & 0xff00) | data; + break; + case 0x0f: + m_irq_count = (m_irq_count & 0x00ff) | (data << 8); + m_irq_enable = 1; + break; + } +} + +/*------------------------------------------------- + + BTL-DRAGONNINJA + + Games: Dragon Ninja (Bootleg), Super Mario Bros. 8 + + iNES: mapper 222 + + In MESS: Unsupported. + + -------------------------------------------------*/ + +/* Scanline based IRQ ? */ +void nes_btl_dn_device::hblank_irq(int scanline, int vblank, int blanked ) +{ + if (!m_irq_count || ++m_irq_count < 240) + return; + + m_irq_count = 0; + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); +} + +WRITE8_MEMBER(nes_btl_dn_device::write_h) +{ + UINT8 bank; + LOG_MMC(("btl_dn write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7003) + { + case 0x0000: + prg8_89(data); + break; + case 0x1000: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 0x2000: + prg8_ab(data); + break; + case 0x3000: + case 0x3002: + case 0x4000: + case 0x4002: + case 0x5000: + case 0x5002: + case 0x6000: + case 0x6002: + bank = ((offset & 0x7000) - 0x3000) / 0x0800 + ((offset & 0x0002) >> 1); + chr1_x(bank, data, CHRROM); + break; + case 0x7000: + m_irq_count = data; + break; + } +} + +/*------------------------------------------------- + + BOOTLEG FC VERSIONS OF FDS GAMES + + -------------------------------------------------*/ + +/*------------------------------------------------- + + WHIRLWIND-2706 + + Games: Meikyuu Jiin Dababa (FDS conversion) and a few + others + + This PCB maps PRG in 0x6000-0x7fff + + iNES: mapper 108 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_whirl2706_device::write_h) +{ + LOG_MMC(("whirl2706 write_h, offset: %04x, data: %02x\n", offset, data)); + m_latch = data; +} + +READ8_MEMBER(nes_whirl2706_device::read_m) +{ + LOG_MMC(("whirl2706 read_m, offset: %04x\n", offset)); + return m_prg[(m_latch * 0x2000 + (offset & 0x1fff)) & (m_prg_size - 1)]; +} + +/*------------------------------------------------- + + Bootleg Board SMB2J + + Games: Super Mario Bros. 2 Pirate (LF36) + + iNES: mapper 43 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_smb2j_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (m_irq_count == 0xfff) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + m_irq_enable = 0; + m_irq_count = 0; + } + else + m_irq_count++; + } + } +} + +WRITE8_MEMBER(nes_smb2j_device::write_l) +{ + LOG_MMC(("smb2j write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset == 0x122) // $4122 + m_irq_enable = data & 3; // maybe also m_irq_count = 0?!? +} + +WRITE8_MEMBER(nes_smb2j_device::write_h) +{ + LOG_MMC(("smb2j write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset == 0x122) // $8122 too? + m_irq_enable = data & 3; +} + +WRITE8_MEMBER(nes_smb2j_device::write_ex) +{ + LOG_MMC(("smb2j write_ex, offset: %04x, data: %02x\n", offset, data)); + + if (offset == 2) + { + int temp = 0; + + // According to hardware tests + if (data & 1) + temp = 3; + else + temp = 4 + ((data & 7) >> 1); + + prg8_cd(temp); + } +} + +READ8_MEMBER(nes_smb2j_device::read_l) +{ + LOG_MMC(("smb2j read_l, offset: %04x\n", offset)); + offset += 0x100; + + if (offset >= 0x1000) + return m_prg[0x10000 + (offset & 0x0fff)]; + + return m_open_bus; // open bus +} + +READ8_MEMBER(nes_smb2j_device::read_m) +{ + LOG_MMC(("smb2j read_m, offset: %04x\n", offset)); + return m_prg[0x4000 + offset]; +} + +/*------------------------------------------------- + + BTL-SMB2A + + Games: Super Mario Bros. 2 Pirate (Jpn version of SMB2) + + iNES: mapper 40 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_smb2ja_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (m_irq_count == 0xfff) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + m_irq_enable = 0; + m_irq_count = 0; + } + else + m_irq_count++; + } + } +} + +WRITE8_MEMBER(nes_smb2ja_device::write_h) +{ + LOG_MMC(("smb2ja write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6000) + { + case 0x0000: + m_irq_enable = 0; + m_irq_count = 0; + break; + case 0x2000: + m_irq_enable = 1; + break; + case 0x6000: + prg8_cd(data); + break; + } +} + +READ8_MEMBER(nes_smb2ja_device::read_m) +{ + LOG_MMC(("smb2ja read_m, offset: %04x\n", offset)); + return m_prg[(0xfe * 0x2000 + (offset & 0x1fff)) & (m_prg_size - 1)]; +} + +/*------------------------------------------------- + + BTL-SMB2B + + Games: Super Mario Bros. 2 Pirate (Jpn version of SMB2) + + This was marked as Alt. Levels. is it true? + + iNES: mapper 50 + + In MESS: Partially Supported. + + -------------------------------------------------*/ + +void nes_smb2jb_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (m_irq_count == 0xfff) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + m_irq_enable = 0; + m_irq_count = 0; + } + else + m_irq_count++; + } + } +} + +WRITE8_MEMBER(nes_smb2jb_device::write_l) +{ + UINT8 prg; + LOG_MMC(("smb2jb write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + switch (offset & 0x1e0) + { + case 0x020: + case 0x0a0: + prg = (data & 0x08) | ((data & 0x06) >> 1) | ((data & 0x01) << 2); + prg8_cd(prg); + break; + case 0x120: + case 0x1a0: + m_irq_enable = data & 0x01; + break; + } +} + +READ8_MEMBER(nes_smb2jb_device::read_m) +{ + LOG_MMC(("smb2jb read_m, offset: %04x\n", offset)); + return m_prg[((0x0f * 0x2000) + (offset & 0x1fff)) & (m_prg_size - 1)]; +} + +/* This goes to 0x4020-0x403f & 0x40a0-0x40bf */ +WRITE8_MEMBER(nes_smb2jb_device::write_ex) +{ + UINT8 prg; + LOG_MMC(("smb2jb write_ex, offset: %04x, data: %02x\n", offset, data)); + + if ((offset < 0x20) || (offset >= 0x80 && offset < 0xa0)) + { + prg = (data & 0x08) | ((data & 0x06) >> 1) | ((data & 0x01) << 2); + prg8_cd(prg); + } +} + +/*------------------------------------------------- + + (UNL-)09-034A + + Games: Zanac FDS conversion with two PRG chips and + no CHRROM and Volleyball FDS conversion with two PRG + chips and CHRROM. + Originally dumps were marked as UNL-SMB2J pcb + + iNES: + + In MESS: Partially Supported. Need to emulate IRQ + (needed by smb2 conversion?) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_09034a_device::write_ex) +{ + LOG_MMC(("09-034a write_ex, offset: %04x, data: %02x\n", offset, data)); + + if (offset == 7) // $4027 + m_reg = data & 1; +} + +READ8_MEMBER(nes_09034a_device::read_m) +{ + LOG_MMC(("09-034a read_m, offset: %04x\n", offset)); + // in 0x6000-0x7fff is mapped the 2nd PRG chip which starts after 32K (hence the +4) + return m_prg[((m_reg + 4) * 0x2000) + offset]; +} + +/*------------------------------------------------- + + Bootleg Board used for FDS conversion + + Games: Tobidase Daisakusen (FDS conversion) + + This PCB maps PRG in 0x6000-0x7fff + + iNES: mapper 120 + + In MESS: Partially Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_tobidase_device::write_l) +{ + LOG_MMC(("tobidase write_h, offset: %04x, data: %02x\n", offset, data)); + offset += 0x4100; + + if ((offset & 0x63c0) == 0x41c0) + m_latch = data & 0x0f; +} + +READ8_MEMBER(nes_tobidase_device::read_m) +{ + LOG_MMC(("tobidase read_m, offset: %04x\n", offset)); + if (m_latch >= 0x0c) + m_latch -= 4; + return m_prg[(m_latch * 0x2000) + (offset & 0x1fff)]; +} + +/*------------------------------------------------- + + UNL-LH32 + + Games: Monty no Doki Doki Daidassou (FDS conversion) + + This PCB maps WRAM in 0xc000-0xdfff and PRG in 0x6000-0x7fff + + iNES: + + In MESS: Supported. + +-------------------------------------------------*/ + +READ8_MEMBER(nes_lh32_device::read_m) +{ + LOG_MMC(("lh32 read_m, offset: %04x\n", offset)); + return m_prg[(m_latch * 0x2000) + (offset & 0x1fff)]; +} + +READ8_MEMBER(nes_lh32_device::read_h) +{ + LOG_MMC(("lh32 read_h, offset: %04x\n", offset)); + + if (offset >= 0x4000 && offset < 0x6000) + return m_prgram[offset & 0x1fff]; + + return hi_access_rom(offset); +} + +WRITE8_MEMBER(nes_lh32_device::write_m) +{ + LOG_MMC(("lh32 write_m, offset: %04x, data: %02x\n", offset, data)); + + if (offset == 0) // 0x6000 only? + { +// printf("write %x\n", data); + m_latch = data & 0xf; + } +} + +WRITE8_MEMBER(nes_lh32_device::write_h) +{ + LOG_MMC(("lh32 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset >= 0x4000 && offset < 0x6000) + m_prgram[offset & 0x1fff] = data; +} + +/*------------------------------------------------- + + UNL-LH10 + + Games: Fuuun Shaolin Kyo (FDS conversion) + + This PCB maps WRAM in 0xc000-0xdfff and PRG in 0x6000-0x7fff + This is very similar to KS7037 (not sure which conversion + uses that one) + + iNES: + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_lh10_device::update_prg() +{ + prg8_89(m_reg[6]); + prg8_ab(m_reg[7]); +} + +READ8_MEMBER(nes_lh10_device::read_m) +{ + LOG_MMC(("lh10 read_m, offset: %04x\n", offset)); + return m_prg[(0x0e * 0x2000) + (offset & 0x1fff)]; +} + +READ8_MEMBER(nes_lh10_device::read_h) +{ + LOG_MMC(("lh10 read_h, offset: %04x\n", offset)); + + if (offset >= 0x4000 && offset < 0x6000) + return m_prgram[offset & 0x1fff]; + + return hi_access_rom(offset); +} + +WRITE8_MEMBER(nes_lh10_device::write_h) +{ + LOG_MMC(("lh10 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset >= 0x4000 && offset < 0x6000) + m_prgram[offset & 0x1fff] = data; + + else + { + switch (offset & 0x6001) + { + case 0x0000: + m_latch = data & 7; + break; + case 0x0001: + m_reg[m_latch] = data; + update_prg(); + break; + } + } +} + +/*------------------------------------------------- + + UNL-LH53 + + Games: Nazo no Murasamejou (FDS conversion) + + This PCB maps WRAM (w/battery) in 0xb800-0xd7ff and + PRG in 0x6000-0x7fff + + iNES: + + In MESS: Preliminar Support only. + + -------------------------------------------------*/ + +void nes_lh53_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + m_irq_count++; + if (m_irq_count > 7560)//value from FCEUMM... + { + m_irq_count = 0; + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + } + } + } +} + +READ8_MEMBER(nes_lh53_device::read_m) +{ + LOG_MMC(("lh53 read_m, offset: %04x\n", offset)); + return m_prg[(m_reg * 0x2000) + (offset & 0x1fff)]; +} + +READ8_MEMBER(nes_lh53_device::read_h) +{ + LOG_MMC(("lh53 read_h, offset: %04x\n", offset)); + + if (offset >= 0x3800 && offset < 0x5800) + return m_battery[offset & 0x1fff]; + + return hi_access_rom(offset); +} + +WRITE8_MEMBER(nes_lh53_device::write_h) +{ + LOG_MMC(("lh53 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset >= 0x3800 && offset < 0x5800) + m_battery[offset & 0x1fff] = data; + + else + { + switch (offset & 0x7000) + { + case 0x6000: + m_irq_enable = BIT(data, 1); + m_irq_count = 0; + if (!m_irq_enable) + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x7000: + m_reg = data & 0x0f; + break; + } + } +} + +/*------------------------------------------------- + + BTL-2708 + + Games: Doki Doki Panic (FDS conversion) + + iNES: mapper 103? + + This board has a very unique setup, with 8KB of WRAM + in 0x6000-0x7fff and other 8KB of WRAM in 0xb800-0xd7ff + which can be switched in 2KB chunks (we attempt to + emulate this by intercepting reads in that area before + they get to the PRG banks...) + + In MESS: Supported. + + -------------------------------------------------*/ + +READ8_MEMBER(nes_2708_device::read_m) +{ + LOG_MMC(("btl-2708 read_m, offset: %04x\n", offset)); + if (!m_reg[1]) + return m_prgram[offset]; // lower 8K of WRAM + else + return m_prg[(m_reg[0] * 0x2000) + (offset & 0x1fff)]; +} + +WRITE8_MEMBER(nes_2708_device::write_m) +{ + LOG_MMC(("btl-2708 write_m, offset: %04x, data: %02x\n", offset, data)); +// if (!m_reg[1]) + m_prgram[offset] = data; // lower 8K of WRAM +} + +READ8_MEMBER(nes_2708_device::read_h) +{ + LOG_MMC(("btl-2708 read_h, offset: %04x\n", offset)); + + if (offset >= 0x3800 && offset < 0x5800 && !m_reg[1]) + return m_prgram[0x2000 + ((offset - 0x3800) & 0x1fff)]; // higher 8K of WRAM + + return hi_access_rom(offset); +} + +WRITE8_MEMBER(nes_2708_device::write_h) +{ + LOG_MMC(("btl-2708 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset >= 0x3800 && offset < 0x5800/* && !m_reg[1]*/) + m_prgram[0x2000 + ((offset - 0x3800) & 0x1fff)] = data; // higher 8K of WRAM + + switch (offset & 0x7000) + { + case 0x0000: + m_reg[0] = data & 0x0f; // PRG bank in 0x6000-0x7fff + break; + case 0x6000: + set_nt_mirroring(BIT(data, 3) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 0x7000: + m_reg[1] = BIT(data, 4); // bit4 enables the two WRAM banks + break; + } +} + +/*------------------------------------------------- + + UNL-AC08 + + Games: Green Beret (FDS conversions) + + iNES: + + This board has two PRG chips. The first (128K) is + connected to 0x6000-0x7fff and switches among the + 16x8K banks; the second (32K) is fixed in 0x8000-0xffff + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_ac08_device::write_ex) +{ + LOG_MMC(("AC-08 write_ex, offset: %04x, data: %02x\n", offset, data)); + if (offset == 5) // $4025 + set_nt_mirroring(!BIT(data, 3) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ); +} + +READ8_MEMBER(nes_ac08_device::read_m) +{ + LOG_MMC(("AC-08 read_m, offset: %04x\n", offset)); + return m_prg[(m_latch * 0x2000) + (offset & 0x1fff)]; +} + +WRITE8_MEMBER(nes_ac08_device::write_h) +{ + LOG_MMC(("AC-08 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset == 1) + m_latch = (data >> 1) & 0x0f; + else + m_latch = data & 0x0f; // apparently there also is a Castlevania FDS conversion using same board with different banking lines +} + +/*------------------------------------------------- + + UNL-BB + + Games: Bubble Bobble and other FDS conversions with CHRROM! + + iNES: + + + In MESS: Supported. + + -------------------------------------------------*/ + +READ8_MEMBER(nes_unl_bb_device::read_m) +{ + LOG_MMC(("unl-bb read_m, offset: %04x\n", offset)); + return m_prg[(((m_reg[0] & 3 & m_prg_mask) * 0x2000) + (offset & 0x1fff))]; +} + +WRITE8_MEMBER(nes_unl_bb_device::write_h) +{ + LOG_MMC(("unl-bb write_h, offset: %04x, data: %02x\n", offset, data)); + + if (!(offset & 0x1000)) + { + m_reg[0] = data; + m_reg[1] = data; + } + else + m_reg[1] = data & 1; // Pro Wrestling uses this + + chr8(m_reg[1] & 3, m_chr_source); +} + +/*------------------------------------------------- + + BTL-MARIO1-MALEE2 (aka Genius Merio Bros) + + Games: Super Mario Bros Malee 2 + + iNES: + + This PCB has two PRG chips (32K+2K) + one CHR chip (8K) + + 2KB of WRAM + The second PRG chip (2K) is connected at 0x6000-0x6800 + while WRAM is at 0x7000-0x7800 + + In MESS: Supported. + + -------------------------------------------------*/ + +READ8_MEMBER(nes_mmalee_device::read_m) +{ + LOG_MMC(("mmalee read_m, offset: %04x\n", offset)); + + if (offset < 0x0800) + return m_prg[0x8000 + offset]; + else if (!m_prgram.empty() && offset >= 0x1000 && offset < 0x1800) // WRAM only in these 2K + return m_prgram[offset & 0x7ff]; + + return ((offset + 0x6000) & 0xff00) >> 8; +} + +WRITE8_MEMBER(nes_mmalee_device::write_m) +{ + LOG_MMC(("mmalee write_m, offset: %04x, data: %02x\n", offset, data)); + + if (!m_prgram.empty() && offset >= 0x1000 && offset < 0x1800) // WRAM only in these 2K + m_prgram[offset & 0x7ff] = data; +} + +/*------------------------------------------------- + + BTL-SHUIGUANPIPE + + Games: Shui Guan Pipe (Gimmick Pirate) + + iNES: + + In MESS: Supported, but there are glitches (PPU or IRQ?) + + -------------------------------------------------*/ + +// timer always running and checking IRQ every 114 CPU cycles? +void nes_shuiguan_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + m_irq_count++; + m_irq_count &= 0xff; + + if (m_irq_enable && !m_irq_count) + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + } +} + +WRITE8_MEMBER(nes_shuiguan_device::write_h) +{ + int bank; + LOG_MMC(("shuiguan write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x0000: + if (offset & 0x800 && !(offset & 0x0c)) // 0x8800-0x8803 + N*0x10 + prg8_89(data); + break; + case 0x1000: + if (offset & 0x800 && !(offset & 0x0c)) // 0x9800-0x9803 + N*0x10 + { + switch (data & 0x03) + { + case 0: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 1: set_nt_mirroring(PPU_MIRROR_HORZ); break; + case 2: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 3: set_nt_mirroring(PPU_MIRROR_HIGH); break; + } + } + break; + case 0x2000: + if (!(offset & 0x800) && !(offset & 0x0c)) // 0xa000-0xa003 + N*0x10 + prg8_cd(data); + if (offset & 0x800 && !(offset & 0x0c)) // 0xa800-0xa803 + N*0x10 + prg8_ab(data); + break; + case 0x3000: + case 0x4000: + case 0x5000: + case 0x6000: + bank = (((offset + 0x1000) >> 11) | (offset >> 3)) & 0x07; + if (offset & 4) + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4); + else + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | ((data & 0x0f) << 0); + chr1_x(bank, m_mmc_vrom_bank[bank], m_chr_source); + break; + case 0x7000: + switch (offset & 0x0c) + { + case 0x00: m_irq_count = (m_irq_count & 0xf0) | ((data & 0x0f) << 0); break; + case 0x04: m_irq_count = (m_irq_count & 0x0f) | ((data & 0x0f) << 4); break; + case 0x08: m_irq_enable= data; break; + case 0x0c: break; + } + break; + } +} + +READ8_MEMBER(nes_shuiguan_device::read_m) +{ + // always first bank?? + LOG_MMC(("shuiguan read_m, offset: %04x\n", offset)); + return m_prg[offset & 0x1fff]; +} diff --git a/src/devices/bus/nes/bootleg.h b/src/devices/bus/nes/bootleg.h new file mode 100644 index 00000000000..d261ba23c31 --- /dev/null +++ b/src/devices/bus/nes/bootleg.h @@ -0,0 +1,480 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_BTL_H +#define __NES_BTL_H + +#include "nxrom.h" + + +// ======================> nes_ax5705_device + +class nes_ax5705_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ax5705_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + void set_prg(); + UINT8 m_mmc_prg_bank[2]; + UINT8 m_mmc_vrom_bank[8]; +}; + + +// ======================> nes_sc127_device + +class nes_sc127_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sc127_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +private: + UINT16 m_irq_count; + int m_irq_enable; +}; + + +// ======================> nes_mbaby_device + +class nes_mbaby_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_mbaby_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; + attotime timer_freq; +}; + + +// ======================> nes_asn_device + +class nes_asn_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_asn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_smb3p_device + +class nes_smb3p_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_smb3p_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT16 m_irq_count; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; +}; + + +// ======================> nes_btl_dn_device + +class nes_btl_dn_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_btl_dn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +private: + UINT16 m_irq_count; +}; + + +// ======================> nes_whirl2706_device + +class nes_whirl2706_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_whirl2706_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_smb2j_device + +class nes_smb2j_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_smb2j_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_ex); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT16 m_irq_count; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; +}; + + +// ======================> nes_smb2ja_device + +class nes_smb2ja_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_smb2ja_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT16 m_irq_count; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; +}; + + +// ======================> nes_smb2jb_device + +class nes_smb2jb_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_smb2jb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_ex); + + virtual void pcb_reset(); + +private: + UINT16 m_irq_count; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; +}; + + +// ======================> nes_09034a_device + +class nes_09034a_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_09034a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_ex); + virtual DECLARE_READ8_MEMBER(read_m); + + virtual void pcb_reset(); + +private: + UINT8 m_reg; +}; + + +// ======================> nes_tobidase_device + +class nes_tobidase_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_tobidase_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_lh32_device + +class nes_lh32_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_lh32_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_lh10_device + +class nes_lh10_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_lh10_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + void update_prg(); + UINT8 m_latch; + UINT8 m_reg[8]; +}; + + +// ======================> nes_lh53_device + +class nes_lh53_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_lh53_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_m) {} + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT16 m_irq_count; + int m_irq_enable; + UINT8 m_reg; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; + attotime timer_freq; +}; + + +// ======================> nes_2708_device + +class nes_2708_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_2708_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[2]; +}; + +// ======================> nes_ac08_device + +class nes_ac08_device : public nes_nrom_device +{ +public: + // nes_ac08_device/destruction + nes_ac08_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_ex); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + +// ======================> nes_unl_bb_device + +class nes_unl_bb_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_unl_bb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[2]; +}; + +// ======================> nes_mmalee_device + +class nes_mmalee_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_mmalee_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + +// ======================> nes_shuiguan_device + +class nes_shuiguan_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_shuiguan_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT16 m_irq_count; + int m_irq_enable; + UINT8 m_mmc_vrom_bank[8]; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; +}; + + + +// device type definition +extern const device_type NES_AX5705; +extern const device_type NES_SC127; +extern const device_type NES_MARIOBABY; +extern const device_type NES_ASN; +extern const device_type NES_SMB3PIRATE; +extern const device_type NES_BTL_DNINJA; +extern const device_type NES_WHIRLWIND_2706; +extern const device_type NES_SMB2J; +extern const device_type NES_SMB2JA; +extern const device_type NES_SMB2JB; +extern const device_type NES_09034A; +extern const device_type NES_TOBIDASE; +extern const device_type NES_LH32; +extern const device_type NES_LH10; +extern const device_type NES_LH53; +extern const device_type NES_2708; +extern const device_type NES_AC08; +extern const device_type NES_UNL_BB; +extern const device_type NES_MMALEE; +extern const device_type NES_SHUIGUAN; + + +#endif diff --git a/src/devices/bus/nes/camerica.c b/src/devices/bus/nes/camerica.c new file mode 100644 index 00000000000..61c41dd7867 --- /dev/null +++ b/src/devices/bus/nes/camerica.c @@ -0,0 +1,225 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Camerica/Codemasters PCBs + + + Here we emulate the following PCBs + + * Camerica BF9093, BF9097, BF909X [mapper 71, two variants] + * Camerica BF9096 Boards [mapper 232] + * Camerica Golden Five [mapper 104] + + Aladdin Deck Enhancer pass-thru cart and the corresponding minicarts + (ALGNV11 & ALGQV11 PCBs) are emulated in a separate source file. + + + TODO: + - check what causes flickering from PPU in Fire Hawk, Poogie and Big Nose (same PPU issue as Back to + Future 2&3?) + - not all the Golden Five games work. investigate! + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "camerica.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_BF9093 = &device_creator; +const device_type NES_BF9096 = &device_creator; +const device_type NES_GOLDEN5 = &device_creator; + + +nes_bf9093_device::nes_bf9093_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BF9093, "NES Cart Camerica BF9093 PCB", tag, owner, clock, "nes_bf9093", __FILE__) +{ +} + +nes_bf9096_device::nes_bf9096_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BF9096, "NES Cart Camerica BF9096 PCB", tag, owner, clock, "nes_bf9096", __FILE__) +{ +} + +nes_golden5_device::nes_golden5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_GOLDEN5, "NES Cart Camerica Golden 5 PCB", tag, owner, clock, "nes_golden5", __FILE__) +{ +} + + + + +void nes_bf9093_device::device_start() +{ + common_start(); +} + +void nes_bf9093_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0xff); + chr8(0, m_chr_source); +} + +void nes_bf9096_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + save_item(NAME(m_bank_base)); +} + +void nes_bf9096_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + chr8(0, m_chr_source); + + m_latch = 0x00; + m_bank_base = 0x0c; + prg16_89ab(m_bank_base | m_latch); + prg16_cdef(m_bank_base | 3); +} + +void nes_golden5_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_golden5_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + chr8(0, m_chr_source); + + m_latch = 0x00; + m_bank_base = 0x00; + prg16_89ab(m_bank_base | m_latch); + prg16_cdef(m_bank_base | 0x0f); +} + + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Camerica Boards (BF9093, BF9097, BF909X, ALGNV11) + + Games: Linus Spacehead's Cosmic Crusade, Micro Machines, + Mig-29, Stunt Kids + + To emulate NT mirroring for BF9097 board (missing in BF9093) + we use crc_hack, however Fire Hawk is broken (but without + mirroring there would be no helicopter graphics). + + iNES: mapper 71 + + In MESS: Partially Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bf9093_device::write_h) +{ + LOG_MMC(("bf9093 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x0000: + case 0x1000: + if (m_pcb_ctrl_mirror) + set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW); + break; + case 0x4000: + case 0x5000: + case 0x6000: + case 0x7000: + prg16_89ab(data); + break; + } +} + +/*------------------------------------------------- + + Camerica BF9096 & ALGQV11 Boards + + Games: Quattro Adventure, Quattro Arcade, Quattro Sports + + Writes to 0x8000-0xbfff set prg block to (data&0x18)>>1, + writes to 0xc000-0xffff set prg page to data&3. selected + prg are: prg16_89ab = block|page, prg_cdef = 3|page. + For more info on the hardware to bypass the NES lockout, see + Kevtris' Camerica Mappers documentation. + + iNES: mapper 232 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bf9096_device::write_h) +{ + LOG_MMC(("bf9096 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 0x4000) + { + m_bank_base = ((data >> 3) & 3) << 2; + prg16_89ab(m_bank_base | m_latch); + prg16_cdef(m_bank_base | 3); + } + else + { + m_latch = data & 3; + prg16_89ab(m_bank_base | m_latch); + } +} + +/*------------------------------------------------- + + Camerica Golden Five board + + Games: Pegasus 5 in 1 + + iNES: mapper 104 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_golden5_device::write_h) +{ + LOG_MMC(("golden5 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 0x4000) + { + if (data & 0x08) + { + m_bank_base = (data & 0x07) << 4; + prg16_89ab(m_bank_base | m_latch); + prg16_cdef(m_bank_base | 0x0f); + } + + } + else + { + m_latch = data & 0x0f; + prg16_89ab(m_bank_base | m_latch); + } +} diff --git a/src/devices/bus/nes/camerica.h b/src/devices/bus/nes/camerica.h new file mode 100644 index 00000000000..7c91bda1f91 --- /dev/null +++ b/src/devices/bus/nes/camerica.h @@ -0,0 +1,72 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_CAMERICA_H +#define __NES_CAMERICA_H + +#include "nxrom.h" + + +// ======================> nes_bf9093_device + +class nes_bf9093_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bf9093_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bf9096_device + +class nes_bf9096_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bf9096_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual DECLARE_WRITE8_MEMBER(write_m) { write_h(space, offset, data, mem_mask); } + + virtual void pcb_reset(); + +private: + UINT8 m_bank_base, m_latch; +}; + + +// ======================> nes_golden5_device + +class nes_golden5_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_golden5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_bank_base, m_latch; +}; + + + + + +// device type definition +extern const device_type NES_BF9093; +extern const device_type NES_BF9096; +extern const device_type NES_GOLDEN5; + +#endif diff --git a/src/devices/bus/nes/cne.c b/src/devices/bus/nes/cne.c new file mode 100644 index 00000000000..76dc0a999f2 --- /dev/null +++ b/src/devices/bus/nes/cne.c @@ -0,0 +1,220 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for C&E PCBs + + + Here we emulate the following PCBs + + * C&E Decathlon [mapper 244] + * C&E Feng Shen Bang [mapper 246] + * C&E Sheng Huo Lie Zhuan [mapper 240] + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "cne.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_CNE_DECATHL = &device_creator; +const device_type NES_CNE_FSB = &device_creator; +const device_type NES_CNE_SHLZ = &device_creator; + + +nes_cne_decathl_device::nes_cne_decathl_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_CNE_DECATHL, "NES Cart C&E Decathlon PCB", tag, owner, clock, "nes_cne_deca", __FILE__) +{ +} + +nes_cne_fsb_device::nes_cne_fsb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_CNE_FSB, "NES Cart C&E Feng Shen Bang PCB", tag, owner, clock, "nes_cne_fsb", __FILE__) +{ +} + +nes_cne_shlz_device::nes_cne_shlz_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_CNE_SHLZ, "NES Cart C&E Sheng Huo Lie Zhuan PCB", tag, owner, clock, "nes_cne_shlz", __FILE__) +{ +} + + + +void nes_cne_decathl_device::device_start() +{ + common_start(); +} + +void nes_cne_decathl_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_cne_fsb_device::device_start() +{ + common_start(); +} + +void nes_cne_fsb_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0xff); + chr8(0, m_chr_source); +} + +void nes_cne_shlz_device::device_start() +{ + common_start(); +} + +void nes_cne_shlz_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + C & E Bootleg Board for Decathlon + + Games: Decathlon + + Pretty simple mapper: writes to 0x8065-0x80a4 set prg32 to + offset & 3; writes to 0x80a5-0x80e4 set chr8 to offset & 7 + + iNES: mapper 244 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_cne_decathl_device::write_h) +{ + LOG_MMC(("cne_decathl_w, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 0x0065) + return; + if (offset < 0x00a5) + { + prg32((offset - 0x0065) & 0x03); + return; + } + if (offset < 0x00e5) + { + chr8((offset - 0x00a5) & 0x07, CHRROM); + } +} + +/*------------------------------------------------- + + C & E Bootleg Board for Fong Shen Bang + + Games: Fong Shen Bang - Zhu Lu Zhi Zhan + + Simple mapper: writes to 0x6000-0x67ff set PRG and CHR banks. + Namely, 0x6000->0x6003 select resp. prg8_89, prg8_ab, prg8_cd + and prg8_ef. 0x6004->0x6007 select resp. crh2_0, chr2_2, + chr2_4 and chr2_6. In 0x6800-0x7fff lies WRAM. Battery backed? + + iNES: mapper 246 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_cne_fsb_device::write_m) +{ + LOG_MMC(("cne_fsb write_m, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 0x0800) + { + switch (offset & 0x0007) + { + case 0x0000: + prg8_89(data); + break; + case 0x0001: + prg8_ab(data); + break; + case 0x0002: + prg8_cd(data); + break; + case 0x0003: + prg8_ef(data); + break; + case 0x0004: + chr2_0(data, CHRROM); + break; + case 0x0005: + chr2_2(data, CHRROM); + break; + case 0x0006: + chr2_4(data, CHRROM); + break; + case 0x0007: + chr2_6(data, CHRROM); + break; + } + } + else + m_battery[offset] = data; +} + +READ8_MEMBER(nes_cne_fsb_device::read_m) +{ + LOG_MMC(("cne_fsb read_m, offset: %04x\n", offset)); + + if (offset >= 0x0800) + return m_battery[offset]; + + return 0xff; +} + +/*------------------------------------------------- + + C & E Bootleg Board for Sheng Huo Lie Zhuan + + Games: Jing Ke Xin Zhuan, Sheng Huo Lie Zhuan + + Simple Mapper: writes to 0x4020-0x5fff sets prg32 to + data>>4 and chr8 to data&f. We currently do not map + writes to 0x4020-0x40ff (to do: verify if this produces + issues) + + iNES: mapper 240 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_cne_shlz_device::write_l) +{ + LOG_MMC(("cne_shlz write_l, offset: %04x, data: %02x\n", offset, data)); + + prg32(data >> 4); + chr8(data & 0x0f, CHRROM); +} diff --git a/src/devices/bus/nes/cne.h b/src/devices/bus/nes/cne.h new file mode 100644 index 00000000000..d00ef06f5cc --- /dev/null +++ b/src/devices/bus/nes/cne.h @@ -0,0 +1,66 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_CNE_H +#define __NES_CNE_H + +#include "nxrom.h" + + +// ======================> nes_cne_decathl_device + +class nes_cne_decathl_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_cne_decathl_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_cne_fsb_device + +class nes_cne_fsb_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_cne_fsb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + + +// ======================> nes_cne_shlz_device + +class nes_cne_shlz_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_cne_shlz_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); +}; + + + + + +// device type definition +extern const device_type NES_CNE_DECATHL; +extern const device_type NES_CNE_FSB; +extern const device_type NES_CNE_SHLZ; + +#endif diff --git a/src/devices/bus/nes/cony.c b/src/devices/bus/nes/cony.c new file mode 100644 index 00000000000..a64063ebc8a --- /dev/null +++ b/src/devices/bus/nes/cony.c @@ -0,0 +1,397 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Cony/Yoko PCBs + + + Here we emulate the following PCBs + + * UNL-CONY [mapper 83] + * UNL-YOKO + + TODO: fix glitches and emulate properly the variants + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "cony.h" + +#include "cpu/m6502/m6502.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_CONY = &device_creator; +const device_type NES_YOKO = &device_creator; + + +nes_cony_device::nes_cony_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_cony_device::nes_cony_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_CONY, "NES Cart Cony PCB", tag, owner, clock, "nes_cony", __FILE__) +{ +} + +nes_yoko_device::nes_yoko_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_cony_device(mconfig, NES_YOKO, "NES Cart Yoko PCB", tag, owner, clock, "nes_yoko", __FILE__) +{ +} + + + + +void nes_cony_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + + save_item(NAME(m_low_reg)); + save_item(NAME(m_reg)); + save_item(NAME(m_extra1)); + save_item(NAME(m_latch1)); + save_item(NAME(m_latch2)); +} + +void nes_cony_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg8_cd(0x1e); + prg8_ef(0x1f); + chr8(0, m_chr_source); + + m_irq_enable = 0; + m_irq_count = 0; + + m_latch1 = 0; + m_latch2 = 0; + m_extra1 = 0; + + memset(m_low_reg, 0, sizeof(m_low_reg)); + memset(m_reg, 0, sizeof(m_reg)); + m_reg[9] = 0x0f; +} + +void nes_yoko_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + + save_item(NAME(m_low_reg)); + save_item(NAME(m_reg)); + save_item(NAME(m_extra1)); + save_item(NAME(m_extra2)); + save_item(NAME(m_latch1)); + save_item(NAME(m_latch2)); +} + +void nes_yoko_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg8_cd(0x1e); + prg8_ef(0x1f); + chr8(0, m_chr_source); + + m_irq_enable = 0; + m_irq_count = 0; + + m_latch1 = 0; + m_latch2 = 0; + m_extra1 = 0; + m_extra2 = 0; + + memset(m_low_reg, 0, sizeof(m_low_reg)); + memset(m_reg, 0, sizeof(m_reg)); + m_reg[9] = 0x0f; +} + + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Cony Bootleg Board + + Games: Dragon Ball Party, Fatal Fury 2, Street Blaster II + Pro, World Heroes 2 + + iNES: mapper 83 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_cony_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (!m_irq_count) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + m_irq_enable = 0; + m_irq_count = 0xffff; + } + else + m_irq_count--; + } + } +} + +WRITE8_MEMBER(nes_cony_device::write_l) +{ + LOG_MMC(("cony write_l, offset: %04x, data: %02x\n", offset, data)); + + if (offset >= 0x1000 && offset < 0x1103) // from 0x5100-0x51ff + m_low_reg[offset & 0x03] = data; +} + +READ8_MEMBER(nes_cony_device::read_l) +{ + LOG_MMC(("cony read_l, offset: %04x\n", offset)); + + if (offset == 0x0f00) // 0x5000 + { + // read dipswitch bit! - currently unimplemented + } + if (offset >= 0x1000 && offset < 0x1103) // from 0x5100-0x51ff + return m_low_reg[offset & 0x03]; + else + return 0x00; +} + +void nes_cony_device::set_prg() +{ + prg16_89ab(m_reg[8] & 0x3f); + prg16_cdef((m_reg[8] & 0x30) | 0x0f); +} + +void nes_cony_device::set_chr() +{ + // FIXME: here we emulate at least 3 different boards!!! + // one board switches 1k VROM banks only + // one writes to 0x8000 and then switches 2k VROM banks only + // one writes to 0x831n (n=2,3,4,5) and then switches 2k VROM banks only + // we should split them and possibly document the proper behavior of each variant + if (m_latch1 && !m_latch2) + { + chr2_0(m_reg[0], CHRROM); + chr2_2(m_reg[1], CHRROM); + chr2_4(m_reg[6], CHRROM); + chr2_6(m_reg[7], CHRROM); + } + else + { + chr1_0(m_reg[0] | ((m_reg[8] & 0x30) << 4), CHRROM); + chr1_1(m_reg[1] | ((m_reg[8] & 0x30) << 4), CHRROM); + chr1_2(m_reg[2] | ((m_reg[8] & 0x30) << 4), CHRROM); + chr1_3(m_reg[3] | ((m_reg[8] & 0x30) << 4), CHRROM); + chr1_4(m_reg[4] | ((m_reg[8] & 0x30) << 4), CHRROM); + chr1_5(m_reg[5] | ((m_reg[8] & 0x30) << 4), CHRROM); + chr1_6(m_reg[6] | ((m_reg[8] & 0x30) << 4), CHRROM); + chr1_7(m_reg[7] | ((m_reg[8] & 0x30) << 4), CHRROM); + } +} + +WRITE8_MEMBER(nes_cony_device::write_h) +{ + LOG_MMC(("cony write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset) + { + case 0x0000: + m_latch1 = 1; + case 0x3000: + case 0x30ff: + case 0x31ff: + m_reg[8] = data; + set_prg(); + set_chr(); + break; + case 0x0100: + m_extra1 = data & 0x80; + switch (data & 0x03) + { + case 0: + set_nt_mirroring(PPU_MIRROR_VERT); + break; + case 1: + set_nt_mirroring(PPU_MIRROR_HORZ); + break; + case 2: + set_nt_mirroring(PPU_MIRROR_LOW); + break; + case 3: + set_nt_mirroring(PPU_MIRROR_HIGH); + break; + } + break; + case 0x0200: + m_irq_count = (m_irq_count & 0xff00) | data; + break; + case 0x0201: + m_irq_enable = m_extra1; + m_irq_count = (data << 8) | (m_irq_count & 0xff); + break; + case 0x0300: + prg8_89(data); + break; + case 0x0301: + prg8_ab(data); + break; + case 0x0302: + prg8_cd(data); + break; + case 0x0312: + case 0x0313: + case 0x0314: + case 0x0315: + m_latch2 = 1; + case 0x0310: + case 0x0311: + case 0x0316: + case 0x0317: + m_reg[offset - 0x0310] = data; + set_chr(); + break; + case 0x0318: + m_reg[9] = data; // unused? + set_prg(); + break; + } +} + +/*------------------------------------------------- + + Yoko Bootleg Board + + Games: Mortal Kombat II, Master Figther VI' + + + Very similar to Cony board + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_yoko_device::write_l) +{ + LOG_MMC(("yoko write_l, offset: %04x, data: %02x\n", offset, data)); + + if (offset >= 0x1300) // from 0x5400 + m_low_reg[offset & 0x03] = data; +} + +READ8_MEMBER(nes_yoko_device::read_l) +{ + LOG_MMC(("yoko read_l, offset: %04x\n", offset)); + + if (offset >= 0x0f00 && offset < 0x1300) // 0x5000 + { + // read dipswitch bit! - currently unimplemented + } + if (offset >= 0x1300) // from 0x5400 + return m_low_reg[offset & 0x03]; + else + return 0x00; +} + +void nes_yoko_device::set_prg() +{ + if (m_extra1 & 0x10) + { + int base = (m_extra2 & 0x08) << 1; + prg8_89(base | (m_reg[0] & 0x0f)); + prg8_ab(base | (m_reg[1] & 0x0f)); + prg8_cd(base | (m_reg[2] & 0x0f)); + prg8_ef(base | 0x0f); + } + else if (m_extra1 & 0x08) + prg32(m_extra2 >> 1); + else + { + prg16_89ab(m_extra2); + prg16_cdef(0xff); + } +} + +void nes_yoko_device::set_chr() +{ + chr2_0(m_reg[4], CHRROM); + chr2_2(m_reg[5], CHRROM); + chr2_4(m_reg[6], CHRROM); + chr2_6(m_reg[7], CHRROM); +} + +WRITE8_MEMBER(nes_yoko_device::write_h) +{ + LOG_MMC(("yoko write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x0c17) + { + case 0x0000: + m_extra2 = data; + set_prg(); + break; + case 0x400: + m_extra1 = data; + if (data & 1) + set_nt_mirroring(PPU_MIRROR_HORZ); + else + set_nt_mirroring(PPU_MIRROR_VERT); + set_prg(); + break; + case 0x0800: + m_irq_count = (m_irq_count & 0xff00) | data; + break; + case 0x0801: + m_irq_enable = m_extra1 & 0x80; + m_irq_count = (data << 8) | (m_irq_count & 0xff); + break; + case 0x0c00: + case 0x0c01: + case 0x0c02: + m_reg[offset & 3] = data; + set_prg(); + break; + case 0x0c10: + case 0x0c11: + case 0x0c16: + case 0x0c17: + m_reg[4 + (offset & 3)] = data; + set_chr(); + break; + } +} diff --git a/src/devices/bus/nes/cony.h b/src/devices/bus/nes/cony.h new file mode 100644 index 00000000000..e97cfdee81d --- /dev/null +++ b/src/devices/bus/nes/cony.h @@ -0,0 +1,75 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_CONY_H +#define __NES_CONY_H + +#include "nxrom.h" + + +// ======================> nes_cony_device + +class nes_cony_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_cony_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_cony_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + virtual void set_prg(); + virtual void set_chr(); + + UINT16 m_irq_count; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; + + UINT8 m_latch1, m_latch2; + UINT8 m_low_reg[4]; + UINT8 m_reg[10]; + UINT8 m_extra1; +}; + + +// ======================> nes_yoko_device + +class nes_yoko_device : public nes_cony_device +{ +public: + // construction/destruction + nes_yoko_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + virtual void set_prg(); + virtual void set_chr(); + + UINT8 m_extra2; +}; + + + + + +// device type definition +extern const device_type NES_CONY; +extern const device_type NES_YOKO; + +#endif diff --git a/src/devices/bus/nes/datach.c b/src/devices/bus/nes/datach.c new file mode 100644 index 00000000000..1bf562976ed --- /dev/null +++ b/src/devices/bus/nes/datach.c @@ -0,0 +1,420 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Bandai Datach PCBs + + + Here we emulate the Bandai Datach Joint ROM System [mapper 157]. + The base unit features: a Bandai LZ93D50 + 24C02 EEPROM PCB + + barcode reader + subslot for PRG cart + + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "datach.h" +#include "cpu/m6502/m6502.h" + +#ifdef NES_PCB_DEBUG +#define VERBOSE 1 +#else +#define VERBOSE 0 +#endif + +#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0) + +#define EEPROM_INTERNAL 0 +#define EEPROM_EXTERNAL 1 + + +#define TEST_EEPROM 0 + +//-------------------------------- +// +// Datach Cartslot implementation +// +//-------------------------------- + +//------------------------------------------------- +// sub-cart interface +//------------------------------------------------- + +datach_cart_interface::datach_cart_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig, device), + m_i2cmem(*this, "i2cmem"), + m_rom(NULL) +{ +} + +datach_cart_interface::~datach_cart_interface() +{ +} + +READ8_MEMBER(datach_cart_interface::read) +{ + if (offset < 0x4000) + return m_rom[(m_bank * 0x4000) + (offset & 0x3fff)]; + else + return m_rom[(0x0f * 0x4000) + (offset & 0x3fff)]; +} + +//------------------------------------------------- +// sub-cart slot device +//------------------------------------------------- + +const device_type NES_DATACH_SLOT = &device_creator; + +nes_datach_slot_device::nes_datach_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, NES_DATACH_SLOT, "NES Datach Cartridge Slot", tag, owner, clock, "nes_datach_slot", __FILE__), + device_image_interface(mconfig, *this), + device_slot_interface(mconfig, *this) +{ +} + +nes_datach_slot_device::~nes_datach_slot_device() +{ +} + + +void nes_datach_slot_device::device_start() +{ + m_cart = dynamic_cast(get_card_device()); +} + +READ8_MEMBER(nes_datach_slot_device::read) +{ + if (m_cart) + return m_cart->read(space, offset, mem_mask); + + return 0xff; +} + +bool nes_datach_slot_device::call_load() +{ + if (m_cart) + { + UINT8 *ROM = m_cart->get_cart_base(); + + if (!ROM) + return IMAGE_INIT_FAIL; + + // Existing Datach carts are all 256K, so we only load files of this size + if (software_entry() == NULL) + { + if (length() != 0x40000 && length() != 0x40010) + return IMAGE_INIT_FAIL; + + int shift = length() - 0x40000; + UINT8 temp[0x40010]; + fread(&temp, length()); + memcpy(ROM, temp + shift, 0x40000); + + // double check that iNES files are really mapper 157 + // (or 16, since some older .nes files marked Datach as mapper 16) + if (length() == 0x40010) + { + UINT8 mapper = (temp[6] & 0xf0) >> 4; + mapper |= temp[7] & 0xf0; + if (mapper != 157 && mapper != 16) + { + return IMAGE_INIT_FAIL; + } + } + } + else + { + if (get_software_region_length("rom") != 0x40000) + return IMAGE_INIT_FAIL; + + memcpy(ROM, get_software_region("rom"), 0x40000); + } + } + + return IMAGE_INIT_PASS; +} + + +bool nes_datach_slot_device::call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry) +{ + load_software_part_region(*this, swlist, swname, start_entry ); + return TRUE; +} + +void nes_datach_slot_device::get_default_card_software(std::string &result) +{ + // any way to detect the game with X24C01? + software_get_default_slot(result, "datach_rom"); +} + + +//-------------------------------- +// +// Datach Minicart implementation +// +// Two kinds of PCB exist +// * ROM only, used by most games +// * ROM + X24C01 EEPROM, used by +// Battle Rush +// +//-------------------------------- + +ROM_START( datach_rom ) + ROM_REGION(0x40000, "datachrom", ROMREGION_ERASEFF) +ROM_END + +const device_type NES_DATACH_ROM = &device_creator; +const device_type NES_DATACH_24C01 = &device_creator; + +nes_datach_rom_device::nes_datach_rom_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) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + datach_cart_interface( mconfig, *this ) +{ +} + +nes_datach_rom_device::nes_datach_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, NES_DATACH_ROM, "NES Datach ROM", tag, owner, clock, "nes_datach_rom", __FILE__), + datach_cart_interface( mconfig, *this ) +{ +} + +nes_datach_24c01_device::nes_datach_24c01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_datach_rom_device(mconfig, NES_DATACH_24C01, "NES Datach + 24C01 PCB", tag, owner, clock, "nes_datach_ep1", __FILE__) +{ +} + + +void nes_datach_rom_device::device_start() +{ + m_rom = (UINT8*)memregion("datachrom")->base(); + save_item(NAME(m_bank)); +} + +void nes_datach_rom_device::device_reset() +{ + m_bank = 0; +} + +const rom_entry *nes_datach_rom_device::device_rom_region() const +{ + return ROM_NAME( datach_rom ); +} + +UINT8 *nes_datach_rom_device::get_cart_base() +{ + return m_rom; +} + + +MACHINE_CONFIG_FRAGMENT( subcart_i2c_24c01 ) + MCFG_24C01_ADD("i2cmem") +MACHINE_CONFIG_END + +machine_config_constructor nes_datach_24c01_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( subcart_i2c_24c01 ); +} + + +//--------------------------------- +// +// Datach Base Unit implementation +// +//--------------------------------- + +const device_type NES_DATACH = &device_creator; + + +nes_datach_device::nes_datach_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_lz93d50_device(mconfig, NES_DATACH, "NES Cart Bandai Datach PCB", tag, owner, clock, "nes_datach", __FILE__), + m_i2cmem(*this, "i2cmem"), + m_reader(*this, "datach"), + m_subslot(*this, "datach_slot") +{ +} + + +void nes_datach_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + serial_timer = timer_alloc(TIMER_SERIAL); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + serial_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1000)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_datach_latch)); +} + +void nes_datach_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_count = 0; + m_datach_latch = 0; + m_i2c_in_use = EEPROM_INTERNAL; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Bandai LZ93D50 + Datach barcode reader emulation + + Games: Datach Games + + iNES: mappers 157 + + In MESS: Supported + + TODO: Datach carts should actually be handled + separately! Original carts were minicarts to be + inserted in a smaller slot of the Barcode reader + FC cart. The Barcode reader acts as a passthrough + but it has no internal ROM (it does not work if + you don't have any minicart inserted) + + TODO2: This class should be derived from the + LZ93D50 + X24C02 class, since the main board + has this EEPROM. Moreover, Datach - Battle Rush + has a second X24C01 EEPROM that we don't emulate yet... + + -------------------------------------------------*/ + + +READ8_MEMBER(nes_datach_device::read_m) +{ + LOG_MMC(("Datach read_m, offset: %04x\n", offset)); + UINT8 i2c_val = 0; +#if TEST_EEPROM + if (m_i2c_dir) + { + if (m_i2c_in_use == EEPROM_INTERNAL) + i2c_val = (m_i2cmem->read_sda() & 1) << 4; + if (m_i2c_in_use == EEPROM_EXTERNAL && m_subslot->m_cart && m_subslot->m_cart->m_i2cmem) + i2c_val = (m_subslot->m_cart->m_i2cmem->read_sda() & 1) << 4; + } +#endif + return m_datach_latch | i2c_val; +} + + +READ8_MEMBER(nes_datach_device::read_h) +{ + LOG_MMC(("Datach read_h, offset: %04x\n", offset)); + // this shall be the proper code, but it's a bit slower, so we access directly the subcart below + //return m_subslot->read(space, offset, mem_mask); + + if (m_subslot->m_cart) + return m_subslot->m_cart->read(space, offset, mem_mask); + else // this is "fake" in the sense that we fill CPU space with 0xff if no Datach cart is loaded + return hi_access_rom(offset); +} + +WRITE8_MEMBER(nes_datach_device::write_h) +{ + LOG_MMC(("Datach write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x0f) + { + case 0: case 1: case 2: case 3: + case 4: case 5: case 6: case 7: + // these don't switch CHR bank (if you try this, both Battle Rush and SD Gundam Wars will have glitches!) + // bit3 goes to SCL of the external EEPROM (and we use write=1 to enable reading from this EEPROM) + // docs from naruko don't specify the bit, our choice comes from observation of writes performed by Battle Rush +#if TEST_EEPROM + if (m_subslot->m_cart && m_subslot->m_cart->m_i2cmem) + { + if (BIT(data, 3)) + m_i2c_in_use = EEPROM_EXTERNAL; + m_subslot->m_cart->m_i2cmem->write_scl(BIT(data, 3)); + } +#endif + break; + case 0x08: + m_subslot->write_prg_bank(data & 0x0f); + break; + case 0x0d: +#if TEST_EEPROM + // bit7, select SDA direction LZ93D50P -> EEPROM or EEPROM -> LZ93D50P + m_i2c_dir = BIT(data, 7); + + // bit6 goes to SDA line, which is in common with the 2nd EEPROM, if present + m_i2cmem->write_sda(BIT(data, 6)); + if (m_subslot->m_cart && m_subslot->m_cart->m_i2cmem) + m_subslot->m_cart->m_i2cmem->write_sda(BIT(data, 6)); + + // bit5 goes to SCL of the internal EEPROM (and we use write=1 to enable reading from this EEPROM) + if (BIT(data, 5)) + m_i2c_in_use = EEPROM_INTERNAL; + m_i2cmem->write_scl(BIT(data, 5)); +#endif + break; + default: + fcg_write(space, offset & 0x0f, data, mem_mask); + break; + } +} + +//------------------------------------------------- +// BARCODE READER + CART SLOT + X24C02 +//------------------------------------------------- + +static SLOT_INTERFACE_START(datach_cart) + SLOT_INTERFACE_INTERNAL("datach_rom", NES_DATACH_ROM) + SLOT_INTERFACE_INTERNAL("datach_ep1", NES_DATACH_24C01) +SLOT_INTERFACE_END + + +MACHINE_CONFIG_FRAGMENT( bandai_datach ) + MCFG_BARCODE_READER_ADD("datach") + MCFG_DATACH_MINICART_ADD("datach_slot", datach_cart) + MCFG_24C02_ADD("i2cmem") +MACHINE_CONFIG_END + +machine_config_constructor nes_datach_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( bandai_datach ); +} + + +//------------------------------------------------- +// device_timer - handler timer events +//------------------------------------------------- + +void nes_datach_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + // 16bit counter, IRQ fired when the counter goes from 1 to 0 + // after firing, the counter is *not* reloaded, but next clock + // counter wraps around from 0 to 0xffff + if (!m_irq_count) + m_irq_count = 0xffff; + else + m_irq_count--; + + if (!m_irq_count) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + m_irq_enable = 0; + } + } + } + if (id == TIMER_SERIAL) + { + m_datach_latch = (m_reader->read_pixel() << 3); + } +} diff --git a/src/devices/bus/nes/datach.h b/src/devices/bus/nes/datach.h new file mode 100644 index 00000000000..b804c079a6f --- /dev/null +++ b/src/devices/bus/nes/datach.h @@ -0,0 +1,170 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_DATACH_H +#define __NES_DATACH_H + +#include "bandai.h" +#include "machine/i2cmem.h" +#include "machine/bcreader.h" + +//-------------------------------- +// +// Datach Cartslot implementation +// +//-------------------------------- + +// ======================> datach_cart_interface + +class datach_cart_interface : public device_slot_card_interface +{ +public: + // construction/destruction + datach_cart_interface(const machine_config &mconfig, device_t &device); + virtual ~datach_cart_interface(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read); + + UINT8 *get_cart_base() { return m_rom; } + void write_prg_bank(UINT8 bank) { m_bank = bank; } + + optional_device m_i2cmem; + +protected: + // internal state + UINT8 *m_rom; + // ROM is accessed via two 16K banks, but only the first one can be switched + UINT8 m_bank; +}; + +// ======================> nes_datach_slot_device + +class nes_datach_slot_device : public device_t, + public device_image_interface, + public device_slot_interface +{ +public: + // construction/destruction + nes_datach_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual ~nes_datach_slot_device(); + + // device-level overrides + virtual void device_start(); + virtual void device_config_complete() { update_names(); } + + // image-level overrides + virtual bool call_load(); + virtual bool call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry); + + virtual iodevice_t image_type() const { return IO_CARTSLOT; } + virtual bool is_readable() const { return 1; } + virtual bool is_writeable() const { return 0; } + virtual bool is_creatable() const { return 0; } + virtual bool must_be_loaded() const { return 0; } + virtual bool is_reset_on_load() const { return 1; } + virtual const char *image_interface() const { return "datach_cart"; } + virtual const char *file_extensions() const { return "nes,bin"; } + virtual const option_guide *create_option_guide() const { return NULL; } + + // slot interface overrides + virtual void get_default_card_software(std::string &result); + + virtual DECLARE_READ8_MEMBER(read); + void write_prg_bank(UINT8 bank) { if (m_cart) m_cart->write_prg_bank(bank); } + + datach_cart_interface* m_cart; +}; + +// device type definition +extern const device_type NES_DATACH_SLOT; + + +#define MCFG_DATACH_MINICART_ADD(_tag, _slot_intf) \ + MCFG_DEVICE_ADD(_tag, NES_DATACH_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, NULL, false) + + +//-------------------------------- +// +// Datach Minicart implementation +// +//-------------------------------- + +// ======================> nes_datach_rom_device + +class nes_datach_rom_device : public device_t, + public datach_cart_interface +{ +public: + // construction/destruction + nes_datach_rom_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_datach_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // optional information overrides + virtual const rom_entry *device_rom_region() const; + virtual UINT8* get_cart_base(); + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); +}; + +// ======================> nes_datach_24c01_device + +class nes_datach_24c01_device : public nes_datach_rom_device +{ +public: + // construction/destruction + nes_datach_24c01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // optional information overrides + virtual machine_config_constructor device_mconfig_additions() const; +}; + +// device type definition +extern const device_type NES_DATACH_ROM; +extern const device_type NES_DATACH_24C01; + + +//--------------------------------- +// +// Datach Base Unit implementation +// +//--------------------------------- + +// ======================> nes_datach_device + +class nes_datach_device : public nes_lz93d50_device +{ +public: + // construction/destruction + nes_datach_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual machine_config_constructor device_mconfig_additions() const; + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + UINT8 m_datach_latch; + required_device m_i2cmem; + required_device m_reader; + required_device m_subslot; + UINT8 m_i2c_dir; + UINT8 m_i2c_in_use; + + static const device_timer_id TIMER_SERIAL = 1; + emu_timer *serial_timer; +}; + + +// device type definition +extern const device_type NES_DATACH; + +#endif diff --git a/src/devices/bus/nes/discrete.c b/src/devices/bus/nes/discrete.c new file mode 100644 index 00000000000..e0109a84d94 --- /dev/null +++ b/src/devices/bus/nes/discrete.c @@ -0,0 +1,209 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for PCBs mostly based on discrete components + + Here we emulate the following PCBs + + * PCB with IC 74x161x161x32 [mapper 70 & 152] + * PCB with IC 74x139x74 [mapper 87] + * PCB with IC 74x377 [mapper 11] + * PCB with IC 74x161x138 [mapper 38] + + TODO: + - Investigating missing inputs in Crime Busters + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "discrete.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_74X161X161X32 = &device_creator; +const device_type NES_74X139X74 = &device_creator; +const device_type NES_74X377 = &device_creator; +const device_type NES_74X161X138 = &device_creator; + + +nes_74x161x161x32_device::nes_74x161x161x32_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_74X161X161X32, "NES Cart Discrete Logic (74*161/161/32) PCB", tag, owner, clock, "nes_74x161", __FILE__) +{ +} + +nes_74x139x74_device::nes_74x139x74_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_74X139X74, "NES Cart Discrete Logic (74*139/74) PCB", tag, owner, clock, "nes_74x139", __FILE__) +{ +} + +nes_74x377_device::nes_74x377_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_74X377, "NES Cart Discrete Logic (74*377) PCB", tag, owner, clock, "nes_74x377", __FILE__) +{ +} + +nes_74x161x138_device::nes_74x161x138_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_74X161X138, "NES Cart Discrete Logic (74*161/138) PCB", tag, owner, clock, "nes_bitcorp_dis", __FILE__) +{ +} + + + + +void nes_74x161x161x32_device::device_start() +{ + common_start(); +} + +void nes_74x161x161x32_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); +} + +void nes_74x139x74_device::device_start() +{ + common_start(); +} + +void nes_74x139x74_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); +} + +void nes_74x377_device::device_start() +{ + common_start(); +} + +void nes_74x377_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_74x161x138_device::device_start() +{ + common_start(); +} + +void nes_74x161x138_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Discrete Logic board IC 74x161x161x32 + + There are two variants (one with hardwired mirroring, the + other with a mirroring control), making necessary two distinct + mappers & pcb_id + + iNES: mappers 70 & 152 + + -------------------------------------------------*/ + +// there are two 'variants' depending on hardwired or mapper ctrl mirroring +WRITE8_MEMBER(nes_74x161x161x32_device::write_h) +{ + LOG_MMC(("74x161x161x32 write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + if (m_pcb_ctrl_mirror) + set_nt_mirroring(BIT(data, 7) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW); + chr8(data, CHRROM); + prg16_89ab(data >> 4); +} + +/*------------------------------------------------- + + Discrete Logic board IC 74x139x74 by Konami & Jaleco + + iNES: mapper 87 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_74x139x74_device::write_m) +{ + LOG_MMC(("74x139x74 write_m, offset: %04x, data: %02x\n", offset, data)); + + chr8(((data & 0x02) >> 1) | ((data & 0x01) << 1), CHRROM); +} + +/*------------------------------------------------- + + Discrete Logic board IC 74x377 by Color Dreams / Nina-007 emulation + + Games: many Color Dreams and Wisdom Tree titles + + iNES: mapper 11 + + In MESS: Supported + + Note: bit2 & bit3 are actually related to CIC lockout + defeating, and real Color Dreams titles use only + bit0 & bit1 for PRG switching. Our usage allows for support + of extended PRG games (e.g. homebrew) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_74x377_device::write_h) +{ + LOG_MMC(("74x377 write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict, but not the prototype of Secret Scout, which actually breaks in case of conflict... + data = account_bus_conflict(offset, data); + + chr8(data >> 4, m_chr_source); + prg32(data & 0x0f); +} + +/*------------------------------------------------- + + Discrete Logic board IC 74x161x138 + + Games: Crime Busters + + iNES: mapper 38 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_74x161x138_device::write_m) +{ + LOG_MMC(("74x161x138 write_m, offset: %04x, data: %02x\n", offset, data)); + + chr8(data >> 2, CHRROM); + prg32(data); +} diff --git a/src/devices/bus/nes/discrete.h b/src/devices/bus/nes/discrete.h new file mode 100644 index 00000000000..1fb615c38f2 --- /dev/null +++ b/src/devices/bus/nes/discrete.h @@ -0,0 +1,80 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_DISCRETE_H +#define __NES_DISCRETE_H + +#include "nxrom.h" + + +// ======================> nes_74x161x161x32_device + +class nes_74x161x161x32_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_74x161x161x32_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_74x139x74_device + +class nes_74x139x74_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_74x139x74_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + + +// ======================> nes_74x377_device + +class nes_74x377_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_74x377_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_74x161x138_device + +class nes_74x161x138_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_74x161x138_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + + + +// device type definition +extern const device_type NES_74X161X161X32; +extern const device_type NES_74X139X74; +extern const device_type NES_74X377; +extern const device_type NES_74X161X138; + +#endif diff --git a/src/devices/bus/nes/disksys.c b/src/devices/bus/nes/disksys.c new file mode 100644 index 00000000000..45cb275e9de --- /dev/null +++ b/src/devices/bus/nes/disksys.c @@ -0,0 +1,431 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Disk System expansion + + + Here we emulate the RAM expansion + Disk Drive which form the + Famicom Disk System. + + Based on info from NESDev wiki ( http://wiki.nesdev.com/w/index.php/Family_Computer_Disk_System ) + + TODO: + - convert floppy drive + fds format to modern code! + - add sound bits + - stop IRQ from using HOLD_LINE + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "disksys.h" +#include "cpu/m6502/m6502.h" +#include "imagedev/flopdrv.h" +#include "formats/nes_dsk.h" + +#ifdef NES_PCB_DEBUG + #define VERBOSE 1 +#else + #define VERBOSE 0 +#endif + +#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0) + + +//----------------------------------------------- +// +// Disk drive implementation +// +//----------------------------------------------- + +static const floppy_interface nes_floppy_interface = +{ + FLOPPY_STANDARD_5_25_DSHD, + LEGACY_FLOPPY_OPTIONS_NAME(nes_only), + "floppy_5_25" +}; + +static MACHINE_CONFIG_FRAGMENT( nes_disksys ) + MCFG_LEGACY_FLOPPY_DRIVE_ADD(FLOPPY_0, nes_floppy_interface) +MACHINE_CONFIG_END + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor nes_disksys_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( nes_disksys ); +} + + + +ROM_START( disksys ) + ROM_REGION(0x2000, "drive", 0) + ROM_SYSTEM_BIOS( 0, "2c33a-01a", "Famicom Disk System Bios") + ROMX_LOAD( "rp2c33a-01a.bin", 0x0000, 0x2000, CRC(5e607dcf) SHA1(57fe1bdee955bb48d357e463ccbf129496930b62), ROM_BIOS(1)) // newer, Nintendo logo has no shadow + ROM_SYSTEM_BIOS( 1, "2c33-01", "Famicom Disk System Bios, older") + ROMX_LOAD( "rp2c33-01.bin", 0x0000, 0x2000, CRC(1c7ae5d5) SHA1(af5af53f66982e749643fdf8b2acbb7d4d3ed229), ROM_BIOS(2)) // older, Nintendo logo has shadow +ROM_END + +//------------------------------------------------- +// rom_region - device-specific ROM region +//------------------------------------------------- + +const rom_entry *nes_disksys_device::device_rom_region() const +{ + return ROM_NAME( disksys ); +} + + +void nes_disksys_device::load_proc(device_image_interface &image) +{ + nes_disksys_device *disk_sys = static_cast(image.device().owner()); + disk_sys->load_disk(image); +} + +void nes_disksys_device::unload_proc(device_image_interface &image) +{ + nes_disksys_device *disk_sys = static_cast(image.device().owner()); + disk_sys->unload_disk(image); +} + + +//------------------------------------------------ +// +// RAM expansion cart implementation +// +//------------------------------------------------ + +const device_type NES_DISKSYS = &device_creator; + + +nes_disksys_device::nes_disksys_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_DISKSYS, "FC RAM Expansion + Disk System PCB", tag, owner, clock, "fc_disksys", __FILE__), + m_fds_data(NULL), + m_disk(*this, FLOPPY_0), + m_fds_sides(0) +{ +} + + +void nes_disksys_device::device_start() +{ + common_start(); + + m_2c33_rom = (UINT8*)memregion("drive")->base(); + + m_disk->floppy_install_load_proc(nes_disksys_device::load_proc); + m_disk->floppy_install_unload_proc(nes_disksys_device::unload_proc); + + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_fds_motor_on)); + save_item(NAME(m_fds_door_closed)); + save_item(NAME(m_fds_current_side)); + save_item(NAME(m_fds_head_position)); + save_item(NAME(m_fds_status0)); + save_item(NAME(m_read_mode)); + save_item(NAME(m_drive_ready)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_transfer)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); + + save_item(NAME(m_fds_last_side)); + save_item(NAME(m_fds_count)); +} + +void nes_disksys_device::pcb_reset() +{ + // read accesses in 0x6000-0xffff are always handled by + // cutom code below, so no need to setup the prg... + chr8(0, CHRRAM); + set_nt_mirroring(PPU_MIRROR_VERT); + + m_fds_motor_on = 0; + m_fds_door_closed = 0; + m_fds_current_side = 1; + m_fds_head_position = 0; + m_fds_status0 = 0; + m_read_mode = 0; + m_drive_ready = 0; + m_irq_count = 0; + m_irq_count_latch = 0; + m_irq_enable = 0; + m_irq_transfer = 0; + + m_fds_count = 0; + m_fds_last_side = 0; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + RAM is in 0x6000-0xdfff (32K) + ROM is in 0xe000-0xffff (8K) + + registers + disk drive are accessed in + 0x4020-0x403f (read_ex/write_ex below) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_disksys_device::write_h) +{ + LOG_MMC(("Famicom Disk System write_h, offset %04x, data: %02x\n", offset, data)); + + if (offset < 0x6000) + m_prgram[offset + 0x2000] = data; +} + +READ8_MEMBER(nes_disksys_device::read_h) +{ + LOG_MMC(("Famicom Disk System read_h, offset: %04x\n", offset)); + + if (offset < 0x6000) + return m_prgram[offset + 0x2000]; + else + return m_2c33_rom[offset & 0x1fff]; +} + +WRITE8_MEMBER(nes_disksys_device::write_m) +{ + LOG_MMC(("Famicom Disk System write_m, offset: %04x, data: %02x\n", offset, data)); + m_prgram[offset] = data; +} + +READ8_MEMBER(nes_disksys_device::read_m) +{ + LOG_MMC(("Famicom Disk System read_m, offset: %04x\n", offset)); + return m_prgram[offset]; +} + +void nes_disksys_device::hblank_irq(int scanline, int vblank, int blanked) +{ + if (m_irq_transfer) + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); +} + +WRITE8_MEMBER(nes_disksys_device::write_ex) +{ + LOG_MMC(("Famicom Disk System write_ex, offset: %04x, data: %02x\n", offset, data)); + + if (offset >= 0x20 && offset < 0x60) + { + // wavetable + } + + switch (offset) + { + case 0x00: + m_irq_count_latch = (m_irq_count_latch & 0xff00) | data; + break; + case 0x01: + m_irq_count_latch = (m_irq_count_latch & 0x00ff) | (data << 8); + break; + case 0x02: + m_irq_count = m_irq_count_latch; + m_irq_enable = BIT(data, 1); + break; + case 0x03: + // bit0 - Enable disk I/O registers + // bit1 - Enable sound I/O registers + break; + case 0x04: + // write data out to disk + // TEST! + if (m_fds_data && m_fds_current_side && !m_read_mode) + m_fds_data[(m_fds_current_side - 1) * 65500 + m_fds_head_position++] = data; + break; + case 0x05: + // $4025 - FDS Control + // bit0 - Drive Motor Control (0: Stop motor; 1: Turn on motor) + // bit1 - Transfer Reset (Set 1 to reset transfer timing to the initial state) + // bit2 - Read / Write mode (0: write; 1: read) + // bit3 - Mirroring (0: horizontal; 1: vertical) + // bit4 - CRC control (set during CRC calculation of transfer) + // bit5 - Always set to '1' + // bit6 - Read/Write Start (Set to 1 when the drive becomes ready for read/write) + // bit7 - Interrupt Transfer (0: Transfer without using IRQ; 1: Enable IRQ when + // the drive becomes ready) + m_fds_motor_on = BIT(data, 0); + + if (BIT(data, 1)) + m_fds_head_position = 0; + + if (!(data & 0x40) && m_drive_ready && m_fds_head_position > 2) + m_fds_head_position -= 2; // ??? is this some sort of compensation?? + + m_read_mode = BIT(data, 2); + set_nt_mirroring(BIT(data, 3) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + m_drive_ready = data & 0x40; + m_irq_transfer = BIT(data, 7); + break; + case 0x06: + // external connector + break; + case 0x60: // $4080 - Volume envelope - read through $4090 + case 0x62: // $4082 - Frequency low + case 0x63: // $4083 - Frequency high + case 0x64: // $4084 - Mod envelope - read through $4092 + case 0x65: // $4085 - Mod counter + case 0x66: // $4086 - Mod frequency low + case 0x67: // $4087 - Mod frequency high + case 0x68: // $4088 - Mod table write + case 0x69: // $4089 - Wave write / master volume + case 0x6a: // $408a - Envelope speed + break; + } +} + +READ8_MEMBER(nes_disksys_device::read_ex) +{ + LOG_MMC(("Famicom Disk System read_ex, offset: %04x\n", offset)); + UINT8 ret = 0x00; + + if (offset >= 0x20 && offset < 0x60) + { + // wavetable + } + + switch (offset) + { + case 0x10: + // $4030 - disk status 0 + // bit0 - Timer Interrupt (1: an IRQ occurred) + // bit1 - Byte transfer flag (Set to 1 every time 8 bits have been transfered between + // the RAM adaptor & disk drive through $4024/$4031; Reset to 0 when $4024, + // $4031, or $4030 has been serviced) + // bit4 - CRC control (0: CRC passed; 1: CRC error) + // bit6 - End of Head (1 when disk head is on the most inner track) + // bit7 - Disk Data Read/Write Enable (1 when disk is readable/writable) + ret = m_fds_status0 | 0x80; + // clear the disk IRQ detect flag + m_fds_status0 &= ~0x01; + break; + case 0x11: + // $4031 - data latch + // don't read data if disk is unloaded + if (!m_fds_data) + ret = 0; + else if (m_fds_current_side && m_read_mode) + { + ret = m_fds_data[(m_fds_current_side - 1) * 65500 + m_fds_head_position++]; + if (m_fds_head_position == 65500) + { + printf("end of disk reached!\n"); + m_fds_status0 |= 0x40; + m_fds_head_position -= 2; + } + } + else + ret = 0; + break; + case 0x12: + // $4032 - disk status 1: + // bit0 - Disk flag (0: Disk inserted; 1: Disk not inserted) + // bit1 - Ready flag (0: Disk ready; 1: Disk not ready) + // bit2 - Protect flag (0: Not write protected; 1: Write protected or disk ejected) + if (!m_fds_data) + ret = 1; + else if (m_fds_last_side != m_fds_current_side) + { + // If we've switched disks, report "no disk" for a few reads + ret = 1; + m_fds_count++; + if (m_fds_count == 50) + { + m_fds_last_side = m_fds_current_side; + m_fds_count = 0; + } + } + else + ret = (m_fds_current_side == 0) ? 1 : 0; // 0 if a disk is inserted + break; + case 0x13: + // $4033 - external connector (bits 0-6) + battery status (bit 7) + ret = 0x80; + break; + case 0x70: // $4090 - Volume gain - write through $4080 + case 0x72: // $4092 - Mod gain - read through $4084 + default: + ret = 0x00; + break; + } + + return ret; +} + +//------------------------------------------------- +// device_timer - handler timer events +//------------------------------------------------- + +void nes_disksys_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable && m_irq_count) + { + m_irq_count--; + if (!m_irq_count) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + m_irq_enable = 0; + m_fds_status0 |= 0x01; + m_irq_count_latch = 0; // used in Kaettekita Mario Bros + } + } + } +} + + +// Hacky helper to allow user to switch disk side with a simple key + +void nes_disksys_device::disk_flip_side() +{ + m_fds_current_side++; + if (m_fds_current_side > m_fds_sides) + m_fds_current_side = 0; + + if (m_fds_current_side == 0) + popmessage("No disk inserted."); + else + popmessage("Disk set to side %d", m_fds_current_side); +} + + + +// Disk Loading / Unloading + +void nes_disksys_device::load_disk(device_image_interface &image) +{ + int header = 0; + m_fds_sides = 0; + + if (image.length() % 65500) + header = 0x10; + + m_fds_sides = (image.length() - header) / 65500; + + if (!m_fds_data) + m_fds_data = auto_alloc_array(machine(), UINT8, m_fds_sides * 65500); + + // if there is an header, skip it + image.fseek(header, SEEK_SET); + image.fread(m_fds_data, 65500 * m_fds_sides); + return; +} + +void nes_disksys_device::unload_disk(device_image_interface &image) +{ + /* TODO: should write out changes here as well */ + m_fds_sides = 0; +} diff --git a/src/devices/bus/nes/disksys.h b/src/devices/bus/nes/disksys.h new file mode 100644 index 00000000000..dcb2bd75c0c --- /dev/null +++ b/src/devices/bus/nes/disksys.h @@ -0,0 +1,71 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_DISKSYS_H +#define __NES_DISKSYS_H + +#include "nxrom.h" +#include "imagedev/flopdrv.h" + + +// ======================> nes_disksys_device + +class nes_disksys_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_disksys_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual const rom_entry *device_rom_region() const; + + virtual DECLARE_READ8_MEMBER(read_ex); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_ex); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void disk_flip_side(); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + + static void load_proc(device_image_interface &image); + static void unload_proc(device_image_interface &image); + +private: + UINT8 *m_2c33_rom; + UINT8 *m_fds_data; // here, we store a copy of the disk + required_device m_disk; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; + + void load_disk(device_image_interface &image); + void unload_disk(device_image_interface &image); + + UINT16 m_irq_count, m_irq_count_latch; + int m_irq_enable, m_irq_transfer; + + UINT8 m_fds_motor_on; + UINT8 m_fds_door_closed; + UINT8 m_fds_current_side; + UINT32 m_fds_head_position; + UINT8 m_fds_status0; + UINT8 m_read_mode; + UINT8 m_drive_ready; + + UINT8 m_fds_sides; + int m_fds_last_side; + int m_fds_count; +}; + + + +// device type definition +extern const device_type NES_DISKSYS; + +#endif diff --git a/src/devices/bus/nes/event.c b/src/devices/bus/nes/event.c new file mode 100644 index 00000000000..4059b107a24 --- /dev/null +++ b/src/devices/bus/nes/event.c @@ -0,0 +1,239 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Nintendo NES-EVENT PCB + + + Here we emulate the following PCBs + + * Nintendo NES-EVENT [mapper 105] + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "event.h" + +#include "cpu/m6502/m6502.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_EVENT = &device_creator; + + +nes_event_device::nes_event_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sxrom_device(mconfig, NES_EVENT, "NES Cart Event PCB", tag, owner, clock, "nes_event", __FILE__), + m_dsw(*this, "DIPSW") +{ +} + + +void nes_event_device::device_start() +{ + common_start(); + event_timer = timer_alloc(TIMER_EVENT); + event_timer->adjust(attotime::never); + timer_freq = machine().device("maincpu")->cycles_to_attotime(1); + + save_item(NAME(m_latch)); + save_item(NAME(m_count)); + save_item(NAME(m_reg)); + save_item(NAME(m_reg_write_enable)); + save_item(NAME(m_nwc_init)); + + save_item(NAME(m_timer_count)); + save_item(NAME(m_timer_on)); + save_item(NAME(m_timer_enabled)); +} + +void nes_event_device::pcb_reset() +{ + m_latch = 0; + m_count = 0; + m_reg[0] = 0x0f; + m_reg[1] = m_reg[2] = m_reg[3] = 0; + m_reg_write_enable = 1; + m_nwc_init = 2; + + set_nt_mirroring(PPU_MIRROR_HORZ); + chr8(0, CHRRAM); + prg32(0); + m_timer_count = 0; + m_timer_enabled = 0; + m_timer_on = 0; +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Event PCB + + Games: Nintento World Championships + + MMC-1 variant with repurposed register at $a000 and a + lot of discrete components + + iNES: mapper 105 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_event_device::set_chr() +{ + // no CHR switching, there are only 8KB VRAM from the cart +} + +void nes_event_device::set_prg() +{ +// printf("enter with %d and reg1 0x%x - reg3 0x%x\n", m_nwc_init, m_reg[1], m_reg[3]); + // reg[1] is different from base MMC-1! + // bit 0 is ignored, bit1/bit3 are used for PRG switch, bit4 is used for the timer + UINT8 temp = (m_reg[1] >> 1) & 7; + + // initially PRG is fixed, until bit4 of reg1 is set to 1 and then to 0 + switch (m_nwc_init) + { + case 2: + if (m_reg[1] & 0x10) m_nwc_init--; + return; + case 1: + if (~m_reg[1] & 0x10) m_nwc_init--; + return; + } + + if (temp < 4) + prg32(temp); + else + { + // else PRG works similarly to base MMC-1, but only acts on the higher 128KB (2nd PRG ROM) + switch (m_reg[0] & 0x0c) + { + case 0x00: + case 0x04: + prg32(0x04 | ((m_reg[3] >> 1) & 0x03)); + break; + case 0x08: + prg16_89ab(0x08 | 0x00); + prg16_cdef(0x08 | (m_reg[3] & 0x07)); + break; + case 0x0c: + prg16_89ab(0x08 | (m_reg[3] & 0x07)); + prg16_cdef(0x08 | 0x07); + break; + } + } + + // after the init procedure above, bit4 of m_reg[1] is used to init IRQ, by setting and then clearing the bit + // however, there are (bankswitch related?) writes with bit4 cleared before the one 'enabling' the timer, so + // we need the additional m_timer_enabled variable, to avoid starting the timer before its time... + if (m_reg[1] & 0x10) + { + m_timer_enabled = 1; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + } + else + { + if (!m_timer_on && m_timer_enabled) + { + m_timer_count = 0x20000000 | ((m_dsw->read() & 0x0f) << 25); + event_timer->adjust(attotime::zero, 0, timer_freq); + m_timer_on = 1; + } + } +} + +void nes_event_device::update_regs(int reg) +{ + switch (reg) + { + case 0: + switch (m_reg[0] & 0x03) + { + case 0: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 1: set_nt_mirroring(PPU_MIRROR_HIGH); break; + case 2: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 3: set_nt_mirroring(PPU_MIRROR_HORZ); break; + } + set_prg(); + break; + case 1: + set_prg(); + break; + case 2: + set_chr(); + break; + case 3: + set_prg(); + break; + } +} + +//------------------------------------------------- +// Dipswicth +//------------------------------------------------- + +static INPUT_PORTS_START( nwc_dsw ) + PORT_START("DIPSW") + PORT_DIPNAME( 0x0f, 0x04, "Timer" ) PORT_DIPLOCATION("SW:!1,!2,!3,!4") + PORT_DIPSETTING( 0x00, "5:00.4" ) + PORT_DIPSETTING( 0x01, "5:19.2" ) + PORT_DIPSETTING( 0x02, "5:38.0" ) + PORT_DIPSETTING( 0x03, "5:56.7" ) + PORT_DIPSETTING( 0x04, "6:15.5" ) + PORT_DIPSETTING( 0x05, "6:34.3" ) + PORT_DIPSETTING( 0x06, "6:53.1" ) + PORT_DIPSETTING( 0x07, "7:11.9" ) + PORT_DIPSETTING( 0x08, "7:30.6" ) + PORT_DIPSETTING( 0x09, "7:49.4" ) + PORT_DIPSETTING( 0x0a, "8:08.2" ) + PORT_DIPSETTING( 0x0b, "8:27.0" ) + PORT_DIPSETTING( 0x0c, "8:45.8" ) + PORT_DIPSETTING( 0x0d, "9:04.5" ) + PORT_DIPSETTING( 0x0e, "9:23.3" ) + PORT_DIPSETTING( 0x0f, "9:42.1" ) +INPUT_PORTS_END + + + +ioport_constructor nes_event_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( nwc_dsw ); +} + + +//------------------------------------------------- +// device_timer - handler timer events +//------------------------------------------------- + +void nes_event_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_EVENT) + { + m_timer_count--; + if (!m_timer_count) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + event_timer->reset(); + } + } +} diff --git a/src/devices/bus/nes/event.h b/src/devices/bus/nes/event.h new file mode 100644 index 00000000000..31076cec074 --- /dev/null +++ b/src/devices/bus/nes/event.h @@ -0,0 +1,44 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_EVENT_H +#define __NES_EVENT_H + +#include "mmc1.h" + + +// ======================> nes_event_device + +class nes_event_device : public nes_sxrom_device +{ +public: + // construction/destruction + nes_event_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual ioport_constructor device_input_ports() const; + virtual void update_regs(int reg); + + required_ioport m_dsw; + + virtual void pcb_reset(); + +protected: + virtual void set_prg(); + virtual void set_chr(); + int m_nwc_init; + + static const device_timer_id TIMER_EVENT = 0; + emu_timer *event_timer; + attotime timer_freq; + + UINT32 m_timer_count; + int m_timer_on, m_timer_enabled; +}; + + +// device type definition +extern const device_type NES_EVENT; + +#endif diff --git a/src/devices/bus/nes/ggenie.c b/src/devices/bus/nes/ggenie.c new file mode 100644 index 00000000000..f9320a5fbee --- /dev/null +++ b/src/devices/bus/nes/ggenie.c @@ -0,0 +1,274 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Galoob Game Genie PCBs + + + Here we emulate the following PCBs + + * Galoob Game Genie, passthrough hacking cart + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "ggenie.h" +#include "includes/nes.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_GGENIE = &device_creator; + + +nes_ggenie_device::nes_ggenie_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_GGENIE, "NES Cart Game Genie PCB", tag, owner, clock, "nes_ggenie", __FILE__), + m_ggslot(*this, "gg_slot") +{ +} + + +void nes_ggenie_device::device_start() +{ + common_start(); + save_item(NAME(m_gg_bypass)); +} + +void nes_ggenie_device::pcb_start(running_machine &machine, UINT8 *ciram_ptr, bool cart_mounted) +{ + device_nes_cart_interface::pcb_start(machine, ciram_ptr, cart_mounted); + if (m_ggslot->m_cart) + m_ggslot->pcb_start(m_ciram); +} + +void nes_ggenie_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + set_nt_mirroring(PPU_MIRROR_LOW); + m_gg_bypass = 0; + + if (m_ggslot->m_cart) + m_ggslot->m_cart->pcb_reset(); +} + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Game Genie board emulation + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_ggenie_device::write_h) +{ +// LOG_MMC(("axrom write_h, offset: %04x, data: %02x\n", offset, data)); + if (!m_gg_bypass) + { + // From blargg: Codes are written to $8001-800C, starting at $800C and going down to $8001. + // Next, two values are written to $8000. The first specify the kind of codes which have + // been inserted, the second write to $8000 is always zero (all 8 bits). + // This probably disables the boot ROM (the code is executing from RAM at this point). + // Once done, the code jumps to ($FFFC) to begin the game. + // All 12 bytes from $8001-800C are written regardless of how many codes are inserted. + // The value written to $8000 is the only clue as to what codes are actually valid and which + // ones have compare values. + if (offset) + { + int code; + offset -= 1; + code = (offset & 0xc) >> 2; + if (code == 3) + return; // how did we end up here? the GG is not expected to write to $800d-800f! + + switch (offset & 3) + { + case 0: + m_gg_addr[code] |= (data & 0x7f) << 8; + break; + case 1: + m_gg_addr[code] = data; + break; + case 2: + m_gg_comp[code] = data; + break; + case 3: + m_gg_repl[code] = data; + break; + } + return; + } + + if (offset == 0 && data == 0) + { + m_gg_bypass = 1; + m_maincpu->set_pc(0xfffc); + } + else + { + // bit 0 is always set (GG enable?) + m_gg_is_comp[0] = BIT(data, 1); + m_gg_is_comp[1] = BIT(data, 2); + m_gg_is_comp[2] = BIT(data, 3); + m_gg_disable[0] = BIT(data, 4); + m_gg_disable[1] = BIT(data, 5); + m_gg_disable[2] = BIT(data, 6); + // bit 7 is always clear + logerror("Game Genie Summary:\n"); + for (int i = 0; i < 3; i++) + { + logerror("Code %d: %s\n", i, m_gg_disable[i] ? "No" : "Yes"); + if (!m_gg_disable[i]) + { + logerror("\tAddr: 0x%X\n", m_gg_addr[i]); + logerror("\tValue: 0x%X\n", m_gg_repl[i]); + if (m_gg_is_comp[i]) + logerror("\t if equals: 0x%X\n", m_gg_comp[i]); + + } + } + } + } + else + m_ggslot->write_h(space, offset, data, mem_mask); +} + +WRITE8_MEMBER(nes_ggenie_device::write_m) +{ + if (m_gg_bypass && m_ggslot) + m_ggslot->write_m(space, offset, data, mem_mask); +} + +WRITE8_MEMBER(nes_ggenie_device::write_l) +{ + if (m_gg_bypass && m_ggslot) + m_ggslot->write_l(space, offset, data, mem_mask); +} + +READ8_MEMBER(nes_ggenie_device::read_h) +{ + if (m_gg_bypass && m_ggslot->m_cart) + { + UINT8 rom_value = m_ggslot->m_cart->hi_access_rom(offset); + + // check if GG code has to act on this address + for (int i = 0; i < 3; i++) + { + if (!m_gg_disable[i] && offset == m_gg_addr[i]) + { + if (!m_gg_is_comp[i] || (m_gg_is_comp[i] && m_gg_comp[i] == rom_value)) + return m_gg_repl[i]; + } + } + + return rom_value; + } + return hi_access_rom(offset); +} + +READ8_MEMBER(nes_ggenie_device::read_m) +{ + if (m_gg_bypass && m_ggslot->m_cart) + return m_ggslot->m_cart->read_m(space, offset, mem_mask); + + return 0xff; +} + +READ8_MEMBER(nes_ggenie_device::read_l) +{ + if (m_gg_bypass && m_ggslot->m_cart) + return m_ggslot->m_cart->read_l(space, offset, mem_mask); + + return 0xff; +} + +WRITE8_MEMBER(nes_ggenie_device::chr_w) +{ + int bank = offset >> 10; + + if (m_gg_bypass && m_ggslot->m_cart) + { + m_ggslot->m_cart->chr_w(space, offset, data, mem_mask); + return; + } + + if (m_chr_src[bank] == CHRRAM) + m_chr_access[bank][offset & 0x3ff] = data; +} + +READ8_MEMBER(nes_ggenie_device::chr_r) +{ + int bank = offset >> 10; + + if (m_gg_bypass && m_ggslot->m_cart) + return m_ggslot->m_cart->chr_r(space, offset, mem_mask); + + return m_chr_access[bank][offset & 0x3ff]; +} + + +WRITE8_MEMBER(nes_ggenie_device::nt_w) +{ + int page = ((offset & 0xc00) >> 10); + + if (m_gg_bypass && m_ggslot->m_cart) + { + m_ggslot->m_cart->nt_w(space, offset, data, mem_mask); + return; + } + + if (!m_nt_writable[page]) + return; + + m_nt_access[page][offset & 0x3ff] = data; +} + +READ8_MEMBER(nes_ggenie_device::nt_r) +{ + int page = ((offset & 0xc00) >> 10); + + if (m_gg_bypass && m_ggslot->m_cart) + return m_ggslot->m_cart->nt_r(space, offset, mem_mask); + + return m_nt_access[page][offset & 0x3ff]; +} + +//------------------------------------------------- +// MACHINE_CONFIG_FRAGMENT( sub_slot ) +//------------------------------------------------- + +static MACHINE_CONFIG_FRAGMENT( sub_slot ) + MCFG_NES_CARTRIDGE_ADD("gg_slot", nes_cart, NULL) + MCFG_NES_CARTRIDGE_NOT_MANDATORY +MACHINE_CONFIG_END + + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor nes_ggenie_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( sub_slot ); +} diff --git a/src/devices/bus/nes/ggenie.h b/src/devices/bus/nes/ggenie.h new file mode 100644 index 00000000000..e4620e59e09 --- /dev/null +++ b/src/devices/bus/nes/ggenie.h @@ -0,0 +1,58 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_GGENIE_H +#define __NES_GGENIE_H + +#include "nxrom.h" + + +// ======================> nes_ggenie_device + +class nes_ggenie_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ggenie_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual DECLARE_READ8_MEMBER(chr_r); + virtual DECLARE_WRITE8_MEMBER(chr_w); + virtual DECLARE_READ8_MEMBER(nt_r); + virtual DECLARE_WRITE8_MEMBER(nt_w); + + void hblank_irq(int scanline, int vblank, int blanked) { if (m_gg_bypass && m_ggslot->m_cart) m_ggslot->m_cart->hblank_irq(scanline, vblank, blanked); } + void scanline_irq(int scanline, int vblank, int blanked) { if (m_gg_bypass && m_ggslot->m_cart) m_ggslot->m_cart->scanline_irq(scanline, vblank, blanked); } + void ppu_latch(offs_t offset) { if (m_gg_bypass && m_ggslot->m_cart) m_ggslot->m_cart->ppu_latch(offset); } + + virtual machine_config_constructor device_mconfig_additions() const; + + virtual void pcb_reset(); + virtual void pcb_start(running_machine &machine, UINT8 *ciram_ptr, bool cart_mounted); + +private: + // emulate the Game Genie! + required_device m_ggslot; + + int m_gg_bypass; + // GG codes + UINT16 m_gg_addr[3]; + UINT8 m_gg_repl[3]; + UINT8 m_gg_comp[3]; + int m_gg_disable[3]; + int m_gg_is_comp[3]; +}; + + + +// device type definition +extern const device_type NES_GGENIE; + +#endif diff --git a/src/devices/bus/nes/henggedianzi.c b/src/devices/bus/nes/henggedianzi.c new file mode 100644 index 00000000000..5cda60e6938 --- /dev/null +++ b/src/devices/bus/nes/henggedianzi.c @@ -0,0 +1,278 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Henggedianzi PCBs + + + Here we emulate the following PCBs + + * Henggedianzi Super Rich [mapper 177] + * Henggedianzi Xing He Zhan Shi [mapper 179] + * Henggedianzi Shen Hua Jian Yun III + + + TODO: + - investigate relation with some TXC & Waixing boards + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "henggedianzi.h" + +#include "cpu/m6502/m6502.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_HENGG_SRICH = &device_creator; +const device_type NES_HENGG_XHZS = &device_creator; +const device_type NES_HENGG_SHJY3 = &device_creator; + + +nes_hengg_srich_device::nes_hengg_srich_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_HENGG_SRICH, "NES Cart Henggedianzi Super Rich PCB", tag, owner, clock, "nes_hengg_srich", __FILE__) +{ +} + +nes_hengg_xhzs_device::nes_hengg_xhzs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_HENGG_XHZS, "NES Cart Henggedianzi Xing He Zhan Shi PCB", tag, owner, clock, "nes_hengg_xhzs", __FILE__) +{ +} + +nes_hengg_shjy3_device::nes_hengg_shjy3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_HENGG_SHJY3, "NES Cart Henggedianzi Shen Hua Jian Yun III PCB", tag, owner, clock, "nes_hengg_shjy3", __FILE__) +{ +} + + + + +void nes_hengg_srich_device::device_start() +{ + common_start(); +} + +void nes_hengg_srich_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_hengg_xhzs_device::device_start() +{ + common_start(); +} + +void nes_hengg_xhzs_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_hengg_shjy3_device::device_start() +{ + common_start(); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_chr_mode)); + save_item(NAME(m_mmc_prg_bank)); + save_item(NAME(m_mmc_vrom_bank)); + save_item(NAME(m_mmc_extra_bank)); +} + +void nes_hengg_shjy3_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_count = m_irq_count_latch = 0; + + m_chr_mode = 0; + memset(m_mmc_prg_bank, 0, sizeof(m_mmc_prg_bank)); + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); + memset(m_mmc_extra_bank, 0, sizeof(m_mmc_extra_bank)); +} + + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Bootleg Board by Henggedianzi + + Games: Mei Guo Fu Hao, Shang Gu Shen Jian , Wang Zi Fu + Chou Ji + + Writes to 0x8000-0xffff set prg32. Moreover, data&0x20 sets + NT mirroring. + + iNES: mapper 177 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_hengg_srich_device::write_h) +{ + LOG_MMC(("hengg_srich write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(data); + set_nt_mirroring(BIT(data, 5) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +/*------------------------------------------------- + + Bootleg Board by Henggedianzi + + Games: Xing He Zhan Shi + + Writes to 0x5000-0x5fff set prg32 banks, writes to 0x8000- + 0xffff set NT mirroring + + Note: NEStopia marks this as Xjzb, but Xing Ji Zheng Ba + (Phantasy Star?) runs on the other Henggedianzi board + Is there an alt dump of Xing Ji Zheng Ba using this? + + iNES: mapper 179 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_hengg_xhzs_device::write_l) +{ + LOG_MMC(("hengg_xhzs write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x4100; + + if (offset & 0x5000) + prg32(data >> 1); +} + +WRITE8_MEMBER(nes_hengg_xhzs_device::write_h) +{ + LOG_MMC(("hengg_xhzs write_h, offset: %04x, data: %02x\n", offset, data)); + + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +/*------------------------------------------------- + + UNL-SHJY3 + + -------------------------------------------------*/ + +/* I think the IRQ should only get fired if enough CPU cycles have passed, but we don't implement (yet) this part */ +void nes_hengg_shjy3_device::hblank_irq(int scanline, int vblank, int blanked) +{ + if (m_irq_enable & 0x02) + { + if (m_irq_count == 0xff) + { + m_irq_count = m_irq_count_latch; + m_irq_enable = m_irq_enable | ((m_irq_enable & 0x01) << 1); + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + } + else + m_irq_count++; + } +} + +void nes_hengg_shjy3_device::update_banks() +{ + prg8_89(m_mmc_prg_bank[0]); + prg8_ab(m_mmc_prg_bank[1]); + + for (int i = 0; i < 8; i++) + { + UINT8 chr_bank = m_mmc_vrom_bank[i] | (m_mmc_extra_bank[i] << 4); + if (m_mmc_vrom_bank[i] == 0xc8) + { + m_chr_mode = 0; + continue; + } + else if (m_mmc_vrom_bank[i] == 0x88) + { + m_chr_mode = 1; + continue; + } + if ((m_mmc_vrom_bank[i] == 4 || m_mmc_vrom_bank[i] == 5) && !m_chr_mode) + chr1_x(i, chr_bank & 1, CHRRAM); + else + chr1_x(i, chr_bank, CHRROM); + } +} + +WRITE8_MEMBER(nes_hengg_shjy3_device::write_h) +{ + LOG_MMC(("shjy3 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset >= 0x3000 && offset <= 0x600c) + { + UINT8 shift = offset & 4; + UINT8 mmc_helper = ((offset & 8) | (offset >> 8)) >> 3; + mmc_helper += 2; + mmc_helper &= 7; + + m_mmc_vrom_bank[mmc_helper] = (m_mmc_vrom_bank[mmc_helper] & (0xf0 >> shift)) | ((data & 0x0f) << shift); + if (shift) + m_mmc_extra_bank[mmc_helper] = data >> 4; + } + else + { + switch (offset) + { + case 0x0010: + m_mmc_prg_bank[0] = data; + break; + case 0x2010: + m_mmc_prg_bank[1] = data; + break; + case 0x1400: + switch (data & 0x03) + { + case 0: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 1: set_nt_mirroring(PPU_MIRROR_HORZ); break; + case 2: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 3: set_nt_mirroring(PPU_MIRROR_HIGH); break; + } + break; + case 0x7000: + m_irq_count_latch = (m_irq_count_latch & 0xf0) | (data & 0x0f); + break; + case 0x7004: + m_irq_count_latch = (m_irq_count_latch & 0x0f) | ((data & 0x0f) << 4); + break; + case 0x7008: + m_irq_enable = data & 0x03; + if (m_irq_enable & 0x02) + m_irq_count = m_irq_count_latch; + break; + } + } + update_banks(); +} diff --git a/src/devices/bus/nes/henggedianzi.h b/src/devices/bus/nes/henggedianzi.h new file mode 100644 index 00000000000..a229ddbda4a --- /dev/null +++ b/src/devices/bus/nes/henggedianzi.h @@ -0,0 +1,78 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_HENGGEDIANZI_H +#define __NES_HENGGEDIANZI_H + +#include "nxrom.h" + + +// ======================> nes_hengg_srich_device + +class nes_hengg_srich_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_hengg_srich_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_hengg_xhzs_device + +class nes_hengg_xhzs_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_hengg_xhzs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_hengg_shjy3_device + +class nes_hengg_shjy3_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_hengg_shjy3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +private: + void update_banks(); + + UINT16 m_irq_count, m_irq_count_latch; + int m_irq_enable; + + int m_chr_mode; + UINT8 m_mmc_prg_bank[2]; + UINT8 m_mmc_vrom_bank[8]; + UINT8 m_mmc_extra_bank[8]; +}; + + + + + +// device type definition +extern const device_type NES_HENGG_SRICH; +extern const device_type NES_HENGG_XHZS; +extern const device_type NES_HENGG_SHJY3; + +#endif diff --git a/src/devices/bus/nes/hes.c b/src/devices/bus/nes/hes.c new file mode 100644 index 00000000000..ddfda0590d8 --- /dev/null +++ b/src/devices/bus/nes/hes.c @@ -0,0 +1,87 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for HES PCBs + + + Here we emulate the HES PCBs (both the one with hardwired mirroring and the one with mapper-controlled + mirroring used by HES 6 in 1) [mapper 113] + + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "hes.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_HES = &device_creator; + + +nes_hes_device::nes_hes_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_HES, "NES Cart HES PCB", tag, owner, clock, "nes_hes", __FILE__) +{ +} + + +void nes_hes_device::device_start() +{ + common_start(); +} + +void nes_hes_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Bootleg Board by HES (also used by others) + + Games: AV Hanafuda Club, AV Soccer, Papillon, Sidewinder, + Total Funpack + + Actually, two variant: one for HES 6-in-1 with mirroring control + and one for AV Soccer and others with hardwired mirroring + + iNES: mapper 113 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_hes_device::write_l) +{ + LOG_MMC(("hes write_l, offset: %04x, data: %02x\n", offset, data)); + + if (!(offset & 0x100)) + { + prg32((data & 0x38) >> 3); + chr8((data & 0x07) | ((data & 0x40) >> 3), CHRROM); + if (m_pcb_ctrl_mirror) + set_nt_mirroring(BIT(data, 7) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ); + } +} diff --git a/src/devices/bus/nes/hes.h b/src/devices/bus/nes/hes.h new file mode 100644 index 00000000000..6a895a8ec1e --- /dev/null +++ b/src/devices/bus/nes/hes.h @@ -0,0 +1,28 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_HES_H +#define __NES_HES_H + +#include "nxrom.h" + + +// ======================> nes_hes_device + +class nes_hes_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_hes_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); +}; + + +// device type definition +extern const device_type NES_HES; + +#endif diff --git a/src/devices/bus/nes/hosenkan.c b/src/devices/bus/nes/hosenkan.c new file mode 100644 index 00000000000..fae439ac903 --- /dev/null +++ b/src/devices/bus/nes/hosenkan.c @@ -0,0 +1,155 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Hosenkan PCBs + + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "hosenkan.h" + +#include "cpu/m6502/m6502.h" +#include "video/ppu2c0x.h" // this has to be included so that IRQ functions can access PPU_BOTTOM_VISIBLE_SCANLINE + + +#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_HOSENKAN = &device_creator; + + +nes_hosenkan_device::nes_hosenkan_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_HOSENKAN, "NES Cart HOSENKAN PCB", tag, owner, clock, "nes_hosenkan", __FILE__) +{ +} + + + + +void nes_hosenkan_device::device_start() +{ + common_start(); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); + save_item(NAME(m_irq_clear)); + save_item(NAME(m_latch)); +} + +void nes_hosenkan_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32((m_prg_chunks - 1) >> 1); + chr8(0, m_chr_source); + + m_latch = 0; + m_irq_enable = 0; + m_irq_count = m_irq_count_latch = 0; + m_irq_clear = 0; +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Bootleg Board by Hosenkan + + Games: Pocahontas, Super Donkey Kong + + iNES: mapper 182 + + In MESS: Supported. + + -------------------------------------------------*/ + +// same as MMC3! +void nes_hosenkan_device::hblank_irq( int scanline, int vblank, int blanked ) +{ + if (scanline < PPU_BOTTOM_VISIBLE_SCANLINE) + { + int prior_count = m_irq_count; + if ((m_irq_count == 0) || m_irq_clear) + m_irq_count = m_irq_count_latch; + else + m_irq_count--; + + if (m_irq_enable && !blanked && (m_irq_count == 0) && (prior_count || m_irq_clear)) + { + LOG_MMC(("irq fired, scanline: %d (MAME %d, beam pos: %d)\n", scanline, + machine().first_screen()->vpos(), machine().first_screen()->hpos())); + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + } + } + m_irq_clear = 0; +} + +WRITE8_MEMBER(nes_hosenkan_device::write_h) +{ + LOG_MMC(("hosenkan write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7003) + { + case 0x0001: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 0x2000: + m_latch = data; + break; + case 0x4000: + switch (m_latch) + { + case 0: + chr2_0(data >> 1, CHRROM); + break; + case 1: + chr1_5(data, CHRROM); + break; + case 2: + chr2_2(data >> 1, CHRROM); + break; + case 3: + chr1_7(data, CHRROM); + break; + case 4: + prg8_89(data); + break; + case 5: + prg8_ab(data); + break; + case 6: + chr1_4(data, CHRROM); + break; + case 7: + chr1_6(data, CHRROM); + break; + } + break; + case 0x6003: + if (data) + { + m_irq_count = data; + m_irq_enable = 1; + } + else + m_irq_enable = 0; + break; + } +} diff --git a/src/devices/bus/nes/hosenkan.h b/src/devices/bus/nes/hosenkan.h new file mode 100644 index 00000000000..f775a513adb --- /dev/null +++ b/src/devices/bus/nes/hosenkan.h @@ -0,0 +1,39 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_HOSENKAN_H +#define __NES_HOSENKAN_H + +#include "nxrom.h" + + +// ======================> nes_hosenkan_device + +class nes_hosenkan_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_hosenkan_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +private: + UINT16 m_irq_count, m_irq_count_latch; + UINT8 m_irq_clear; + int m_irq_enable; + + UINT8 m_latch; +}; + + + +// device type definition +extern const device_type NES_HOSENKAN; + + +#endif diff --git a/src/devices/bus/nes/irem.c b/src/devices/bus/nes/irem.c new file mode 100644 index 00000000000..43f5bab1d7d --- /dev/null +++ b/src/devices/bus/nes/irem.c @@ -0,0 +1,352 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for IREM PCBs + + + Here we emulate the following PCBs + + * IREM LROG017 [mapper 77] + * IREM Holy Diver [mapper 78] + * IREM TAM-S1 [mapper 97] + * IREM G101 [mapper 32] + * IREM H3001 [mapper 65] + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "irem.h" + +#include "cpu/m6502/m6502.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_LROG017 = &device_creator; +const device_type NES_HOLYDIVR = &device_creator; +const device_type NES_TAM_S1 = &device_creator; +const device_type NES_G101 = &device_creator; +const device_type NES_H3001 = &device_creator; + + +nes_lrog017_device::nes_lrog017_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_LROG017, "NES Cart Irem Discrete 74*161/161/21/138 PCB", tag, owner, clock, "nes_lrog017", __FILE__) +{ +} + +nes_holydivr_device::nes_holydivr_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_HOLYDIVR, "NES Cart Irem Holy Diver PCB", tag, owner, clock, "nes_holydivr", __FILE__) +{ +} + +nes_tam_s1_device::nes_tam_s1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_TAM_S1, "NES Cart Irem TAM-S1 PCB", tag, owner, clock, "nes_tam_s1", __FILE__) +{ +} + +nes_g101_device::nes_g101_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_G101, "NES Cart Irem G-101 PCB", tag, owner, clock, "nes_g101", __FILE__) +{ +} + +nes_h3001_device::nes_h3001_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_H3001, "NES Cart Irem H-3001 PCB", tag, owner, clock, "nes_h3001", __FILE__) +{ +} + + + + +void nes_lrog017_device::device_start() +{ + common_start(); +} + +void nes_lrog017_device::pcb_reset() +{ + prg32(0); + chr2_0(0, CHRROM); + chr2_2(0, CHRRAM); + chr2_4(1, CHRRAM); + chr2_6(2, CHRRAM); +} + +void nes_holydivr_device::device_start() +{ + common_start(); +} + +void nes_holydivr_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); +} + +void nes_tam_s1_device::device_start() +{ + common_start(); +} + +void nes_tam_s1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(m_prg_chunks - 1); + prg16_cdef(0); + chr8(0, m_chr_source); +} + +void nes_g101_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_g101_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; +} + +void nes_h3001_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); +} + +void nes_h3001_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_count = 0; + m_irq_count_latch = 0; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Irem LROG017 - Discrete board emulation (74*161/161/21/138) + + Games: Napoleon Senki + + iNES: mapper 77 + + This board should use 2KB of CHRRAM as NTRAM, instead + of using directly 4-screen mirroring, but for the + moment we cheat in this way... + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_lrog017_device::write_h) +{ + LOG_MMC(("lrog017 write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + prg32(data); + chr2_0((data >> 4), CHRROM); +} + +/*------------------------------------------------- + + Irem Holy Diver board emulation + + iNES: mapper 78 (shared with JF-16) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_holydivr_device::write_h) +{ + LOG_MMC(("holy diver write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + set_nt_mirroring(BIT(data, 3) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ); + chr8(data >> 4, CHRROM); + prg16_89ab(data); +} + +/*------------------------------------------------- + + Irem TAM-S1 board emulation + + Games: Kaiketsu Yanchamaru + + iNES: mapper 97 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_tam_s1_device::write_h) +{ + LOG_MMC(("tam s1 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 0x4000) + { + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + set_nt_mirroring(BIT(data, 7) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ); + prg16_cdef(data); + } +} + +/*------------------------------------------------- + + Irem G-101 board emulation + + Major League uses hardwired mirroring + + iNES: mapper 32 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_g101_device::write_h) +{ + LOG_MMC(("g101 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x0000: + if (m_latch) + { + prg8_89(0xfe); + prg8_cd(data & 0x1f); + } + else + { + prg8_89(data & 0x1f); + prg8_cd(0xfe); + } + break; + case 0x1000: + m_latch = BIT(data, 1); + if (m_pcb_ctrl_mirror) + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 0x2000: + prg8_ab(data & 0x1f); + break; + case 0x3000: + chr1_x(offset & 0x07, data & 0x7f, CHRROM); + break; + } +} + +/*------------------------------------------------- + + Irem H-3001 board emulation + + Games: Daiku no Gen San 2 - Akage no Dan no Gyakushuu, + Kaiketsu Yanchamaru 3, Spartan X 2 + + iNES: mapper 65 + + In MESS: Supported. + + -------------------------------------------------*/ + + +void nes_h3001_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + // 16bit counter, IRQ fired when the counter reaches 0 + // after firing, the counter is *not* reloaded and does not wrap + if (m_irq_count > 0) + m_irq_count--; + + if (!m_irq_count) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + m_irq_enable = 0; + } + } + } +} + +WRITE8_MEMBER(nes_h3001_device::write_h) +{ + LOG_MMC(("h3001 write_h, offset %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7fff) + { + case 0x0000: + prg8_89(data); + break; + + case 0x1001: + set_nt_mirroring(BIT(data, 7) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + + case 0x1003: + m_irq_enable = data & 0x80; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + + case 0x1004: + m_irq_count = m_irq_count_latch; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + + case 0x1005: + m_irq_count_latch = (m_irq_count_latch & 0x00ff) | (data << 8); + break; + + case 0x1006: + m_irq_count_latch = (m_irq_count_latch & 0xff00) | data; + break; + + case 0x2000: + prg8_ab(data); + break; + + case 0x3000: case 0x3001: case 0x3002: case 0x3003: + case 0x3004: case 0x3005: case 0x3006: case 0x3007: + chr1_x(offset & 0x07, data, CHRROM); + break; + + case 0x4000: + prg8_cd(data); + break; + + default: + break; + } +} diff --git a/src/devices/bus/nes/irem.h b/src/devices/bus/nes/irem.h new file mode 100644 index 00000000000..5c7924b4e7d --- /dev/null +++ b/src/devices/bus/nes/irem.h @@ -0,0 +1,108 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_IREM_H +#define __NES_IREM_H + +#include "nxrom.h" + + +// ======================> nes_lrog017_device + +class nes_lrog017_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_lrog017_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_holydivr_device + +class nes_holydivr_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_holydivr_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_tam_s1_device + +class nes_tam_s1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_tam_s1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_g101_device + +class nes_g101_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_g101_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + UINT8 m_latch; +}; + + +// ======================> nes_h3001_device + +class nes_h3001_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_h3001_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + UINT16 m_irq_count, m_irq_count_latch; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; +}; + + + +// device type definition +extern const device_type NES_LROG017; +extern const device_type NES_HOLYDIVR; +extern const device_type NES_TAM_S1; +extern const device_type NES_G101; +extern const device_type NES_H3001; + +#endif diff --git a/src/devices/bus/nes/jaleco.c b/src/devices/bus/nes/jaleco.c new file mode 100644 index 00000000000..28df5250118 --- /dev/null +++ b/src/devices/bus/nes/jaleco.c @@ -0,0 +1,886 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Jaleco PCBs + + + Here we emulate the following PCBs + + * Jaleco JF-11 [mapper 140] + * Jaleco JF-13 [mapper 86] + * Jaleco JF-16 [mapper 78] + * Jaleco JF-17 [mapper 72] + its variant with samples + * Jaleco JF-19 [mapper 92] + its variant with samples + * Jaleco SS88006 [mapper 18] + its variant(s) with samples + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "jaleco.h" + +#include "cpu/m6502/m6502.h" +#include "sound/samples.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_JF11 = &device_creator; +const device_type NES_JF16 = &device_creator; +const device_type NES_JF17 = &device_creator; +const device_type NES_JF19 = &device_creator; +const device_type NES_SS88006 = &device_creator; +const device_type NES_JF13 = &device_creator; +const device_type NES_JF17_ADPCM = &device_creator; +const device_type NES_JF19_ADPCM = &device_creator; +const device_type NES_JF23 = &device_creator; +const device_type NES_JF24 = &device_creator; +const device_type NES_JF29 = &device_creator; +const device_type NES_JF33 = &device_creator; + + +nes_jf11_device::nes_jf11_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_JF11, "NES Cart Jaleco JF-11 PCB", tag, owner, clock, "nes_jf11", __FILE__) +{ +} + +nes_jf13_device::nes_jf13_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_JF13, "NES Cart Jaleco JF-13 PCB", tag, owner, clock, "nes_jf13", __FILE__), + m_samples(*this, "samples") +{ +} + +nes_jf16_device::nes_jf16_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_JF16, "NES Cart Jaleco JF-16 PCB", tag, owner, clock, "nes_jf16", __FILE__) +{ +} + +nes_jf17_device::nes_jf17_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_jf17_device::nes_jf17_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_JF17, "NES Cart Jaleco JF-17 PCB", tag, owner, clock, "nes_jf17", __FILE__) +{ +} + +nes_jf17_adpcm_device::nes_jf17_adpcm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_jf17_device(mconfig, NES_JF17_ADPCM, "NES Cart Jaleco JF-17 + ADPCM (Moero!! Pro Tennis) PCB", tag, owner, clock, "nes_jf17_pcm", __FILE__), + m_samples(*this, "samples") +{ +} + +nes_jf19_device::nes_jf19_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_jf19_device::nes_jf19_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_JF19, "NES Cart Jaleco JF-19 (Moero!! Pro Soccer) PCB", tag, owner, clock, "nes_jf19", __FILE__) +{ +} + +nes_jf19_adpcm_device::nes_jf19_adpcm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_jf19_device(mconfig, NES_JF19_ADPCM, "NES Cart Jaleco JF-19 + ADPCM (Moero!! Pro Yakyuu 88) PCB", tag, owner, clock, "nes_jf19_pcm", __FILE__), + m_samples(*this, "samples") +{ +} + +nes_ss88006_device::nes_ss88006_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_ss88006_device::nes_ss88006_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SS88006, "NES Cart Jaleco SS88006 PCB", tag, owner, clock, "nes_ss88006", __FILE__) +{ +} + +nes_ss88006_adpcm_device::nes_ss88006_adpcm_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_ss88006_device(mconfig, type, name, tag, owner, clock, shortname, source) +{ +} + +nes_jf23_device::nes_jf23_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_ss88006_adpcm_device(mconfig, NES_JF23, "NES Cart Jaleco Shin Moero Pro Yakyuu PCB", tag, owner, clock, "nes_jf23", __FILE__), + m_samples(*this, "samples") +{ +} + +nes_jf24_device::nes_jf24_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_ss88006_adpcm_device(mconfig, NES_JF24, "NES Cart Jaleco Terao no Dosukoi Oozumou PCB", tag, owner, clock, "nes_jf24", __FILE__), + m_samples(*this, "samples") +{ +} + +nes_jf29_device::nes_jf29_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_ss88006_adpcm_device(mconfig, NES_JF29, "NES Cart Jaleco Moe Pro! '90 PCB", tag, owner, clock, "nes_jf29", __FILE__), + m_samples(*this, "samples") +{ +} + +nes_jf33_device::nes_jf33_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_ss88006_adpcm_device(mconfig, NES_JF33, "NES Cart Jaleco Moe Pro! Saikyou-hen PCB", tag, owner, clock, "nes_jf33", __FILE__), + m_samples(*this, "samples") +{ +} + + + +void nes_jf11_device::device_start() +{ + common_start(); +} + +void nes_jf11_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_jf13_device::device_start() +{ + common_start(); +} + +void nes_jf13_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_jf16_device::device_start() +{ + common_start(); +} + +void nes_jf16_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); +} + +void nes_jf17_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_jf17_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; +} + +void nes_jf19_device::device_start() +{ + common_start(); +} + +void nes_jf19_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_ss88006_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_mmc_prg_bank)); + save_item(NAME(m_mmc_vrom_bank)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); + save_item(NAME(m_irq_mode)); + save_item(NAME(m_latch)); +} + +void nes_ss88006_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_prg_bank, 0, sizeof(m_mmc_prg_bank)); + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); + m_irq_enable = 0; + m_irq_mode = 0; + m_irq_count = 0; + m_irq_count_latch = 0; + m_latch = 0; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Jaleco JF-11, JF-12 & JF-14 boards emulation + + Games: Bio Senshi Dan, Mississippi Satsujin Jiken + + iNES: mapper 140 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_jf11_device::write_m) +{ + LOG_MMC(("jf11 write_m, offset: %04x, data: %02x\n", offset, data)); + chr8(data, CHRROM); + prg32(data >> 4); +} + +/*------------------------------------------------- + + Jaleco JF-13 board emulation + + Games: Moero Pro Yakyuu + + Note: we don't emulate the additional sound hardware. + + iNES: mapper 86 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_jf13_device::write_m) +{ + LOG_MMC(("jf13 write_m, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 0x1000) + { + prg32((data >> 4) & 0x03); + chr8(((data >> 4) & 0x04) | (data & 0x03), CHRROM); + } + else + { +// printf("sample write: offset: %04x, data: %02x\n", offset, data); + if (data & 0x20) + m_samples->start(data & 0x0f, data & 0x0f); + else + m_samples->stop_all(); + } +} + +/*------------------------------------------------- + + Jaleco JF-16 board emulation + + iNES: mapper 78 (shared with a diff Irem board) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_jf16_device::write_h) +{ + LOG_MMC(("jf16 write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + set_nt_mirroring(BIT(data, 3) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW); + chr8(data >> 4, CHRROM); + prg16_89ab(data); +} + + +/*------------------------------------------------- + + Jaleco JF-17 boards emulation + + Note: we don't emulate the additional sound hardware + for Moero!! Pro Tennis + + Games: Moero!! Juudou Warriors, Moero!! Pro Tennis, Pinball + Quest Jpn + + iNES: mapper 72 + + In MESS: Supported, see below for the Moero Pro Tennis + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_jf17_device::write_h) +{ + LOG_MMC(("jf17 write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + if (BIT(m_latch, 7) && !BIT(data, 7)) + prg16_89ab(m_latch & 0x07); + if (BIT(m_latch, 6) && !BIT(data, 6)) + chr8(m_latch & 0x0f, CHRROM); + + m_latch = data; +} + +WRITE8_MEMBER(nes_jf17_adpcm_device::write_h) +{ + LOG_MMC(("jf17 + ADPCM write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + if (BIT(m_latch, 7) && !BIT(data, 7)) + prg16_89ab(m_latch & 0x07); + if (BIT(m_latch, 6) && !BIT(data, 6)) + chr8(m_latch & 0x0f, CHRROM); + if (BIT(data, 5) && !BIT(data,4)) + { +// printf("sample write: offset: %04x, data: %02x\n", offset, data); + m_samples->start(offset & 0x1f, offset & 0x1f); + } + + m_latch = data; +} + +/*------------------------------------------------- + + Jaleco JF-19 boards emulation + + Note: we don't emulate the additional sound hardware. + + Games: Moero Pro Soccer, Moero Pro Yakyuu '88 + + iNES: mapper 92 + + In MESS: Supported, see below for the Moero Pro Yakyuu '88 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_jf19_device::write_h) +{ + LOG_MMC(("jf19 write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + if (BIT(data, 7)) + prg16_cdef(data & 0x0f); + if (BIT(data, 6)) + chr8(data & 0x0f, CHRROM); +} + +WRITE8_MEMBER(nes_jf19_adpcm_device::write_h) +{ + LOG_MMC(("jf19 + ADPCM write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + if (BIT(data, 7)) + prg16_cdef(data & 0x0f); + if (BIT(data, 6)) + chr8(data & 0x0f, CHRROM); + if (BIT(data, 5) && !BIT(data,4)) + { +// printf("sample write: offset: %04x, data: %02x\n", offset, data); + m_samples->start(offset & 0x1f, offset & 0x1f); + } +} + +/*------------------------------------------------- + + Jaleco SS88006 board emulation, aka JF-27, JF-29, JF-30, ..., + JF-38, JF-40, JF-41 + + Games: Lord of King, Magic John, Moe Pro '90, Ninja Jajamaru, + Pizza Pop, Plasma Ball + + iNES: mapper 18 + + In MESS: Supported, see below for the games with samples + + -------------------------------------------------*/ + +void nes_ss88006_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (m_irq_mode & 0x08) // 4bits counter + { + if (!(m_irq_count & 0x000f)) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + m_irq_count = (m_irq_count & 0xfff0) | 0x000f; + } + else + m_irq_count = (m_irq_count & 0xfff0) | ((m_irq_count & 0x000f) - 1); + } + else if (m_irq_mode & 0x04) // 8bits counter + { + if (!(m_irq_count & 0x00ff)) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + m_irq_count = (m_irq_count & 0xff00) | 0x00ff; + } + else + m_irq_count = (m_irq_count & 0xff00) | ((m_irq_count & 0x00ff) - 1); + } + else if (m_irq_mode & 0x02) // 12bits counter + { + if (!(m_irq_count & 0x0fff)) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + m_irq_count = (m_irq_count & 0xf000) | 0x0fff; + } + else + m_irq_count = (m_irq_count & 0xf000) | ((m_irq_count & 0x0fff) - 1); + } + else // 16bits counter + { + if (!m_irq_count) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + m_irq_count = 0xffff; + } + else + m_irq_count = m_irq_count - 1; + } + } + } +} + +WRITE8_MEMBER(nes_ss88006_device::ss88006_write) +{ + UINT8 bank; + LOG_MMC(("ss88006 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7003) + { + case 0x0000: + m_mmc_prg_bank[0] = (m_mmc_prg_bank[0] & 0xf0) | (data & 0x0f); + prg8_89(m_mmc_prg_bank[0]); + break; + case 0x0001: + m_mmc_prg_bank[0] = (m_mmc_prg_bank[0] & 0x0f) | (data << 4); + prg8_89(m_mmc_prg_bank[0]); + break; + case 0x0002: + m_mmc_prg_bank[1] = (m_mmc_prg_bank[1] & 0xf0) | (data & 0x0f); + prg8_ab(m_mmc_prg_bank[1]); + break; + case 0x0003: + m_mmc_prg_bank[1] = (m_mmc_prg_bank[1] & 0x0f) | (data << 4); + prg8_ab(m_mmc_prg_bank[1]); + break; + case 0x1000: + m_mmc_prg_bank[2] = (m_mmc_prg_bank[2] & 0xf0) | (data & 0x0f); + prg8_cd(m_mmc_prg_bank[2]); + break; + case 0x1001: + m_mmc_prg_bank[2] = (m_mmc_prg_bank[2] & 0x0f) | (data << 4); + prg8_cd(m_mmc_prg_bank[2]); + break; + + /* $9002, 3 (1002, 3) uncaught = Jaleco Baseball writes 0 */ + /* believe it's related to battery-backed ram enable/disable */ + + case 0x2000: case 0x2001: case 0x2002: case 0x2003: + case 0x3000: case 0x3001: case 0x3002: case 0x3003: + case 0x4000: case 0x4001: case 0x4002: case 0x4003: + case 0x5000: case 0x5001: case 0x5002: case 0x5003: + bank = ((offset & 0x7000) - 0x2000) / 0x0800 + ((offset & 0x0002) >> 1); + if (offset & 0x0001) + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f)<< 4); + else + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | (data & 0x0f); + + chr1_x(bank, m_mmc_vrom_bank[bank], CHRROM); + break; + + case 0x6000: + m_irq_count_latch = (m_irq_count_latch & 0xfff0) | (data & 0x0f); + break; + case 0x6001: + m_irq_count_latch = (m_irq_count_latch & 0xff0f) | ((data & 0x0f) << 4); + break; + case 0x6002: + m_irq_count_latch = (m_irq_count_latch & 0xf0ff) | ((data & 0x0f) << 8); + break; + case 0x6003: + m_irq_count_latch = (m_irq_count_latch & 0x0fff) | ((data & 0x0f) << 12); + break; + case 0x7000: + m_irq_count = m_irq_count_latch; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x7001: + m_irq_enable = data & 0x01; + m_irq_mode = data & 0x0e; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + + case 0x7002: + switch (data & 0x03) + { + case 0: set_nt_mirroring(PPU_MIRROR_HORZ); break; + case 1: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 2: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 3: set_nt_mirroring(PPU_MIRROR_HIGH); break; + } + break; + + default: + logerror("Jaleco SS88006 uncaught write, addr: %04x, value: %02x\n", offset + 0x8000, data); + break; + } +} + + +// bits2-bits6 are sample number, bit1 is setup/enable/disable +// program first write sample # + bit1 set to 'init' the sample +// then it writes sample # + bit1 clear to 'start' the sample +void nes_ss88006_adpcm_device::ss88006_adpcm_write(address_space &space, offs_t offset, UINT8 data, samples_device *dev) +{ + LOG_MMC(("ss88006 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7003) + { + case 0x7003: + if ((m_latch & 0x7c) == (data & 0x7c)) + { +// printf("sample write: data: %02x\n", data); + if ((m_latch & 2) && !(data & 2)) + dev->start((data >> 2) & 0x1f, (data >> 2) & 0x1f); + } + m_latch = data; + break; + + default: + ss88006_write(space, offset, data); + break; + } +} + +/********************************************************** + + Boards with external samples + (due to undumpable UPD7755C/UPD7756C) + + JF-13 (Moero!! Pro Yakyuu) / iNES mapper 86 + JF-17 (Moero!! Pro Tennis) / iNES mapper 72 + ADPCM + JF-19 (Moero!! Pro Yakyuu '88 - Kettei Ban) / iNES mapper 92 + ADPCM + JF-23 (Shin Moero Pro Yakyuu) / iNES mapper 18 + ADPCM + JF-24 (Terao no Dosukoi Oozumou) / iNES mapper 18 + ADPCM + JF-29 (Moe Pro 90) / iNES mapper 18 + ADPCM + JF-33 (Moe Pro Saikyou-hen) / iNES mapper 18 + ADPCM + + +***********************************************************/ + +static const char *const jf13_sample_names[] = +{ + "*moepro", + "00", // strike + "01", // ball + "02", // time + "03", // out + "04", // safe + "05", // foul + "06", // (catcher obtains the ball) + "07", // you're out + "08", // play ball + "09", // ball four + "10", // home run + "11", // new pitcher + "12", // ouch (pitcher hits batter) + "13", // ?? + "14", // (bat hits the ball) + "15", // (crowd) + 0 +}; + +static const char *const jf17_sample_names[] = +{ + "*mptennis", + "00", // NOT EXISTING? + "01", // NOT EXISTING? + "02", // "love" (points) + "03", // 15 (points) + "04", // 30 (points) + "05", // 40 (points) + "06", // advantage + "07", // (advantage) server + "08", // (advantage) receiver + "09", // all (equal points) + "10", // deuce + "11", // game + "12", // (racket hits ball) + "13", // (crowd?) + "14", // fault + "15", // net + "16", // out + "17", // ?? + "18", // (ball hits player) + "19", // NOT EXISTING? + 0 +}; + +static const char *const jf19_sample_names[] = +{ + "*moepro88", + "00", // out + "01", // safe + "02", // foul + "03", // fair + "04", // strike + "05", // ball + "06", // time + "07", // batter out + "08", // ball four + "09", // home run + "10", // play ball + "11", // new pitcher + "12", // pinch-hit + "13", // (hit by pitch) + "14", // (bat hits the ball) + "15", // (bunt) + "16", // NOT EXISTING? + "17", // (catcher obtains the ball) + "18", // (pitcher obtains the ball) + "19", // (crowd) + 0 +}; + +static const char *const jf23_sample_names[] = +{ + "*smoepro", + "00", // out + "01", // safe + "02", // foul + "03", // NOT EXISTING? + "04", // strike + "05", // ball + "06", // time + "07", // batter out + "08", // ball four + "09", // home run + "10", // play ball + "11", // new pitcher + "12", // pinch-hit + "13", // (hit by pitch) + "14", // (bat hits the ball) + "15", // (bunt) + "16", // (catcher obtains the ball) + "17", // fair + "18", // (catcher obtains the ball, alt) + "19", // (crowd) + 0 +}; + +static const char *const jf24_sample_names[] = +{ + "*terao", + "00", // (tree beating sound) + "01", // Hakkyoyoi + "02", // Nokotta + "03", // Matta Nashi + "04", // Nokotta Nokotta + "05", // Matta Arimasen + 0 +}; + +static const char *const jf29_sample_names[] = +{ + "*moepro90", + "00", // out + "01", // safe + "02", // foul + "03", // Not existing? + "04", // strike + "05", // ball + "06", // time + "07", // batter out + "08", // ball four + "09", // home run + "10", // play ball + "11", // new pitcher + "12", // pinch-hit + "13", // (hit by pitch) + "14", // (bat hits the ball) + "15", // (bunt) + "16", // (catcher obtains the ball, alt) + "17", // (catcher obtains the ball) + "18", // (catcher obtains the ball, alt 2) + "19", // (crowd) + 0 +}; + +static const char *const jf33_sample_names[] = +{ + "*mpsaikyo", + "00", // out + "01", // safe + "02", // foul + "03", // ??? + "04", // strike + "05", // ball + "06", // time + "07", // batter out + "08", // ball four + "09", // home run + "10", // play ball + "11", // new pitcher + "12", // pinch-hit + "13", // (hit by pitch) + "14", // (bat hits the ball) + "15", // (bunt) + "16", // (catcher obtains the ball) + "17", // (catcher obtains the ball, alt) + "18", // ?? + "19", // (crowd) + 0 +}; + +//------------------------------------------------- +// MACHINE_DRIVER +//------------------------------------------------- + +static MACHINE_CONFIG_FRAGMENT( jf13 ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("samples", SAMPLES, 0) + MCFG_SAMPLES_CHANNELS(16) + MCFG_SAMPLES_NAMES(jf13_sample_names) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.50) +MACHINE_CONFIG_END + +static MACHINE_CONFIG_FRAGMENT( jf17 ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("samples", SAMPLES, 0) + MCFG_SAMPLES_CHANNELS(20) + MCFG_SAMPLES_NAMES(jf17_sample_names) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.50) +MACHINE_CONFIG_END + +static MACHINE_CONFIG_FRAGMENT( jf19 ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("samples", SAMPLES, 0) + MCFG_SAMPLES_CHANNELS(20) + MCFG_SAMPLES_NAMES(jf19_sample_names) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.50) +MACHINE_CONFIG_END + +static MACHINE_CONFIG_FRAGMENT( jf23 ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("samples", SAMPLES, 0) + MCFG_SAMPLES_CHANNELS(20) + MCFG_SAMPLES_NAMES(jf23_sample_names) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.50) +MACHINE_CONFIG_END + +static MACHINE_CONFIG_FRAGMENT( jf24 ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("samples", SAMPLES, 0) + MCFG_SAMPLES_CHANNELS(6) + MCFG_SAMPLES_NAMES(jf24_sample_names) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.50) +MACHINE_CONFIG_END + +static MACHINE_CONFIG_FRAGMENT( jf29 ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("samples", SAMPLES, 0) + MCFG_SAMPLES_CHANNELS(20) + MCFG_SAMPLES_NAMES(jf29_sample_names) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.50) +MACHINE_CONFIG_END + +static MACHINE_CONFIG_FRAGMENT( jf33 ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("samples", SAMPLES, 0) + MCFG_SAMPLES_CHANNELS(20) + MCFG_SAMPLES_NAMES(jf33_sample_names) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.50) +MACHINE_CONFIG_END + +//------------------------------------------------- +// machine_config_additions +//------------------------------------------------- + +machine_config_constructor nes_jf13_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( jf13 ); +} + +machine_config_constructor nes_jf17_adpcm_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( jf17 ); +} + +machine_config_constructor nes_jf19_adpcm_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( jf19 ); +} + +machine_config_constructor nes_jf23_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( jf23 ); +} + +machine_config_constructor nes_jf24_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( jf24 ); +} + +machine_config_constructor nes_jf29_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( jf29 ); +} + +machine_config_constructor nes_jf33_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( jf33 ); +} diff --git a/src/devices/bus/nes/jaleco.h b/src/devices/bus/nes/jaleco.h new file mode 100644 index 00000000000..facea12b75f --- /dev/null +++ b/src/devices/bus/nes/jaleco.h @@ -0,0 +1,256 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_JALECO_H +#define __NES_JALECO_H + +#include "nxrom.h" +#include "sound/samples.h" + + +// ======================> nes_jf11_device + +class nes_jf11_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_jf11_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + + +// ======================> nes_jf13_device + +class nes_jf13_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_jf13_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); + +private: + required_device m_samples; +}; + + +// ======================> nes_jf16_device + +class nes_jf16_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_jf16_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_jf17_device + +class nes_jf17_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_jf17_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_jf17_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + UINT8 m_latch; +}; + + +// ======================> nes_jf17_adpcm_device + +class nes_jf17_adpcm_device : public nes_jf17_device +{ +public: + // construction/destruction + nes_jf17_adpcm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual machine_config_constructor device_mconfig_additions() const; + virtual DECLARE_WRITE8_MEMBER(write_h); + +private: + required_device m_samples; +}; + + +// ======================> nes_jf19_device + +class nes_jf19_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_jf19_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_jf19_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_jf19_adpcm_device + +class nes_jf19_adpcm_device : public nes_jf19_device +{ +public: + // construction/destruction + nes_jf19_adpcm_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual machine_config_constructor device_mconfig_additions() const; + virtual DECLARE_WRITE8_MEMBER(write_h); + +private: + required_device m_samples; +}; + + +// ======================> nes_ss88006_device + +class nes_ss88006_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ss88006_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_ss88006_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_WRITE8_MEMBER(ss88006_write); + virtual DECLARE_WRITE8_MEMBER(write_h) { ss88006_write(space, offset, data, mem_mask); } + + virtual void pcb_reset(); + +protected: + UINT16 m_irq_count, m_irq_count_latch; + UINT8 m_irq_mode; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; + + UINT8 m_mmc_prg_bank[3]; + UINT8 m_mmc_vrom_bank[8]; + + UINT8 m_latch; // used for samples, in derived classes +}; + + +// ======================> nes_ss88006_adpcm_device + +class nes_ss88006_adpcm_device : public nes_ss88006_device +{ +public: + // construction/destruction + nes_ss88006_adpcm_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); + + // device-level overrides + void ss88006_adpcm_write(address_space &space, offs_t offset, UINT8 data, samples_device *dev); +}; + + +// ======================> nes_jf23_device + +class nes_jf23_device : public nes_ss88006_adpcm_device +{ +public: + // construction/destruction + nes_jf23_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual machine_config_constructor device_mconfig_additions() const; + +private: + required_device m_samples; + virtual DECLARE_WRITE8_MEMBER(write_h) { ss88006_adpcm_write(space, offset, data, m_samples); } +}; + + +// ======================> nes_jf24_device + +class nes_jf24_device : public nes_ss88006_adpcm_device +{ +public: + // construction/destruction + nes_jf24_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual machine_config_constructor device_mconfig_additions() const; + +private: + required_device m_samples; + virtual DECLARE_WRITE8_MEMBER(write_h) { ss88006_adpcm_write(space, offset, data, m_samples); } +}; + + +// ======================> nes_jf29_device + +class nes_jf29_device : public nes_ss88006_adpcm_device +{ +public: + // construction/destruction + nes_jf29_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual machine_config_constructor device_mconfig_additions() const; + +private: + required_device m_samples; + virtual DECLARE_WRITE8_MEMBER(write_h) { ss88006_adpcm_write(space, offset, data, m_samples); } +}; + + +// ======================> nes_jf33_device + +class nes_jf33_device : public nes_ss88006_adpcm_device +{ +public: + // construction/destruction + nes_jf33_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual machine_config_constructor device_mconfig_additions() const; + +private: + required_device m_samples; + virtual DECLARE_WRITE8_MEMBER(write_h) { ss88006_adpcm_write(space, offset, data, m_samples); } +}; + + + + +// device type definition +extern const device_type NES_JF11; +extern const device_type NES_JF13; +extern const device_type NES_JF16; +extern const device_type NES_JF17; +extern const device_type NES_JF17_ADPCM; +extern const device_type NES_JF19; +extern const device_type NES_JF19_ADPCM; +extern const device_type NES_SS88006; +extern const device_type NES_JF23; +extern const device_type NES_JF24; +extern const device_type NES_JF29; +extern const device_type NES_JF33; + +#endif diff --git a/src/devices/bus/nes/jy.c b/src/devices/bus/nes/jy.c new file mode 100644 index 00000000000..31358e2ab16 --- /dev/null +++ b/src/devices/bus/nes/jy.c @@ -0,0 +1,589 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for JY Company + + + Here we emulate multiple PCBs by JY Company with weird IRQ mechanisms [mappers 90, 209, 211] + + TODO: long list... + - add dipswitches + - revamp IRQ system + * scanline/hblank irq should fire 8 times per line (currently not possible) + * implement CPU write IRQ (used by any games?) + * possibly implementing 'funky' IRQ mode (unused?) + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "jy.h" + +#include "cpu/m6502/m6502.h" +#include "video/ppu2c0x.h" // this has to be included so that IRQ functions can access PPU_BOTTOM_VISIBLE_SCANLINE + +#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_JY_TYPEA = &device_creator; +const device_type NES_JY_TYPEB = &device_creator; +const device_type NES_JY_TYPEC = &device_creator; + + +nes_jy_typea_device::nes_jy_typea_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_jy_typea_device::nes_jy_typea_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_JY_TYPEA, "NES Cart JY Company Type A PCB", tag, owner, clock, "nes_jya", __FILE__) +{ +} + +nes_jy_typeb_device::nes_jy_typeb_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_jy_typea_device(mconfig, type, name, tag, owner, clock, shortname, source) +{ +} + +nes_jy_typeb_device::nes_jy_typeb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_jy_typea_device(mconfig, NES_JY_TYPEB, "NES Cart JY Company Type B PCB", tag, owner, clock, "nes_jyb", __FILE__) +{ +} + +nes_jy_typec_device::nes_jy_typec_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_jy_typeb_device(mconfig, NES_JY_TYPEC, "NES Cart JY Company Type C PCB", tag, owner, clock, "nes_jyc", __FILE__) +{ +} + + + + +void nes_jy_typea_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->reset(); + timer_freq = machine().device("maincpu")->cycles_to_attotime(1); + + save_item(NAME(m_mul)); + save_item(NAME(m_latch)); + save_item(NAME(m_mmc_prg_bank)); + save_item(NAME(m_mmc_nt_bank)); + save_item(NAME(m_mmc_vrom_bank)); + save_item(NAME(m_reg)); + save_item(NAME(m_chr_latch)); + save_item(NAME(m_bank_6000)); + + save_item(NAME(m_irq_prescale)); + save_item(NAME(m_irq_prescale_mask)); + save_item(NAME(m_irq_mode)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_flip)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_up)); + save_item(NAME(m_irq_down)); +} + +void nes_jy_typea_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + // 0x5000-0x5fff + m_mul[0] = 0; + m_mul[1] = 0; + m_latch = 0; + + // 0x8000-0xffff + memset(m_mmc_prg_bank, 0xff, sizeof(m_mmc_prg_bank)); + memset(m_mmc_nt_bank, 0, sizeof(m_mmc_nt_bank)); + memset(m_mmc_vrom_bank, 0xffff, sizeof(m_mmc_vrom_bank)); + memset(m_reg, 0, sizeof(m_reg)); + m_chr_latch[0] = 0; + m_chr_latch[1] = 4; + m_bank_6000 = 0; + + update_prg(); + update_chr(); + update_mirror(); + + m_irq_mode = 0; + m_irq_count = 0; + m_irq_prescale = 0; + m_irq_prescale_mask = 0xff; + m_irq_flip = 0; + m_irq_enable = 0; + m_irq_up = 0; + m_irq_down = 0; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + JY Company Type A board emulation + + iNES: mapper 90 + + -------------------------------------------------*/ + + +READ8_MEMBER(nes_jy_typea_device::nt_r) +{ + int page = ((offset & 0xc00) >> 10); + irq_clock(0, 2); + return m_nt_access[page][offset & 0x3ff]; +} + +READ8_MEMBER(nes_jy_typea_device::chr_r) +{ + int bank = offset >> 10; + irq_clock(0, 2); + return m_chr_access[bank][offset & 0x3ff]; +} + +void nes_jy_typea_device::irq_clock(int mode, int blanked) +{ + bool clock = FALSE, fire = FALSE; + + if (m_irq_mode != mode) + return; + + // no counter changes if both Up/Down are set or clear + if ((m_irq_down && m_irq_up) || (!m_irq_down && !m_irq_up)) + return; + + // update prescaler + if (m_irq_down) + { + if ((m_irq_prescale & m_irq_prescale_mask) == 0) + { + clock = TRUE; + m_irq_prescale = (m_irq_prescale_mask == 7) ? ((m_irq_prescale & 0xf8) | 7) : 0xff; + } + else + m_irq_prescale = (m_irq_prescale_mask == 7) ? ((m_irq_prescale & 0xf8) | ((m_irq_prescale - 1) & m_irq_prescale_mask)) : (m_irq_prescale - 1); + } + + if (m_irq_up) + { + if ((m_irq_prescale & m_irq_prescale_mask) == m_irq_prescale_mask) + { + clock = TRUE; + m_irq_prescale = (m_irq_prescale_mask == 7) ? (m_irq_prescale & 0xf8) : 0; + } + else + m_irq_prescale = (m_irq_prescale_mask == 7) ? ((m_irq_prescale & 0xf8) | ((m_irq_prescale + 1) & m_irq_prescale_mask)) : (m_irq_prescale + 1); + } + + // if prescaler wraps, update count + if (clock) + { + if (m_irq_down) + { + if (m_irq_count == 0) + { + fire = TRUE; + m_irq_count = 0xff; + } + else + m_irq_count--; + } + + if (m_irq_up) + { + if (m_irq_count == 0xff) + { + fire = TRUE; + m_irq_count = 0; + } + else + m_irq_count++; + } + + + // if count wraps, check if IRQ is enabled + if (fire && m_irq_enable && !blanked) + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + + } +} + +void nes_jy_typea_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + irq_clock(0, 0); + } +} + +void nes_jy_typea_device::scanline_irq(int scanline, int vblank, int blanked) +{ + if (scanline < PPU_BOTTOM_VISIBLE_SCANLINE) + irq_clock(blanked, 1); +} + + +// 0x5000-0x5fff : sort of protection? +READ8_MEMBER(nes_jy_typea_device::read_l) +{ + LOG_MMC(("JY Company write_m, offset: %04x\n", offset)); + offset += 0x100; + + if (offset >= 0x1000 && offset < 0x1800) + { + // bit6/bit7 DSW read + return m_open_bus & 0x3f; + } + + if (offset >= 0x1800) + { + if ((offset & 7) == 0) + return (m_mul[0] * m_mul[1]) & 0xff; + if ((offset & 7) == 1) + return ((m_mul[0] * m_mul[1]) >> 8) & 0xff; + if ((offset & 7) == 3) + return m_latch; + } + + return m_open_bus; // open bus +} + +WRITE8_MEMBER(nes_jy_typea_device::write_l) +{ + LOG_MMC(("JY Company write_m, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset >= 0x1800) + { + if ((offset & 7) == 0) + m_mul[0] = data; + if ((offset & 7) == 1) + m_mul[1] = data; + if ((offset & 7) == 3) + m_latch = data; + } +} + +// 0x6000-0x7fff : WRAM or open bus +READ8_MEMBER(nes_jy_typea_device::read_m) +{ + LOG_MMC(("JY Company write_m, offset: %04x\n", offset)); + + if (m_reg[0] & 0x80) + return m_prg[(m_bank_6000 & m_prg_mask) * 0x2000 + (offset & 0x1fff)]; + + return m_open_bus; // open bus +} + + +inline UINT8 nes_jy_typea_device::unscramble(UINT8 bank) +{ + return BITSWAP8(bank & 0x7f,7,0,1,2,3,4,5,6); +} + +void nes_jy_typea_device::update_prg() +{ + UINT8 exPrg = (m_reg[3] & 0x06) << 5; + UINT8 last = (m_reg[0] & 0x04) ? m_mmc_prg_bank[3] : 0x3f; + + switch (m_reg[0] & 0x03) + { + case 0: // 32KB + prg32((last & 0x0f) | (exPrg >> 2)); + m_bank_6000 = (((m_mmc_prg_bank[3] * 4) + 3) & 0x3f) | (exPrg >> 2); + break; + + case 1: // 16KB + prg16_89ab((m_mmc_prg_bank[1] & 0x1f) | (exPrg >> 1)); + prg16_cdef((last & 0x1f) | (exPrg >> 1)); + m_bank_6000 = (((m_mmc_prg_bank[3] * 2) + 1) & 0x1f) | (exPrg >> 1); + break; + + case 2: // 8KB + prg8_89(m_mmc_prg_bank[0] | exPrg); + prg8_ab(m_mmc_prg_bank[1] | exPrg); + prg8_cd(m_mmc_prg_bank[2] | exPrg); + prg8_ef(last | exPrg); + m_bank_6000 = m_mmc_prg_bank[3] | exPrg; + break; + + case 3: // 8KB Alt + prg8_89((unscramble(m_mmc_prg_bank[0]) & 0x3f) | exPrg); + prg8_ab((unscramble(m_mmc_prg_bank[1]) & 0x3f) | exPrg); + prg8_cd((unscramble(m_mmc_prg_bank[2]) & 0x3f) | exPrg); + if (m_reg[0] & 0x04) + prg8_ef((unscramble(m_mmc_prg_bank[3]) & 0x3f) | exPrg); + else + prg8_ef((unscramble(last) & 0x3f) | exPrg); + m_bank_6000 = (unscramble(m_mmc_prg_bank[3]) & 0x3f) | exPrg; + break; + } +} + +void nes_jy_typea_device::update_chr() +{ + // in 1KB & 2KB mode, PPU 0x800-0xfff always mirrors 0x000-0x7ff (0x1800-0x1fff not affected) + int chr_mirror_mode = BIT(m_reg[3], 7) << 1; + + // Case (m_reg[3] & 0x20 == 0) + // Block mode enabled: in this case lower bits select a 256KB page inside CHRROM + // and the low bytes of m_mmc_vrom_bank select the banks inside such a page + + // docs suggest m_reg[3] & 0x1f for chr_page below, + // but 45 in 1 (JY-120A) menu requires to use this (from NEStopia) + UINT8 chr_page = (m_reg[3] & 1) | ((m_reg[3] & 0x18) >> 2); + UINT32 extra_chr_base = BIT(m_reg[3], 5) ? 0 : (chr_page * 0x100); + UINT32 extra_chr_mask = BIT(m_reg[3], 5) ? 0xffffff : 0xff; + + switch (m_reg[0] & 0x18) + { + case 0x00: // 8KB + extra_chr_base >>= 3; + extra_chr_mask >>= 3; + chr8(extra_chr_base | (m_mmc_vrom_bank[0] & extra_chr_mask), m_chr_source); + break; + + case 0x08: // 4KB + extra_chr_base >>= 2; + extra_chr_mask >>= 2; + // Type A & B games have fixed m_chr_latch[0] = 0 and m_chr_latch[1] = 4 + // Type C games can change them at each CHR access! + chr4_0(extra_chr_base | (m_mmc_vrom_bank[m_chr_latch[0]] & extra_chr_mask), m_chr_source); + chr4_4(extra_chr_base | (m_mmc_vrom_bank[m_chr_latch[1]] & extra_chr_mask), m_chr_source); + break; + + case 0x10: // 2KB + extra_chr_base >>= 1; + extra_chr_mask >>= 1; + chr2_0(extra_chr_base | (m_mmc_vrom_bank[0] & extra_chr_mask), m_chr_source); + chr2_2(extra_chr_base | (m_mmc_vrom_bank[2 ^ chr_mirror_mode] & extra_chr_mask), m_chr_source); + chr2_4(extra_chr_base | (m_mmc_vrom_bank[4] & extra_chr_mask), m_chr_source); + chr2_6(extra_chr_base | (m_mmc_vrom_bank[6] & extra_chr_mask), m_chr_source); + break; + + case 0x18: // 1KB + chr1_0(extra_chr_base | (m_mmc_vrom_bank[0] & extra_chr_mask), m_chr_source); + chr1_1(extra_chr_base | (m_mmc_vrom_bank[1] & extra_chr_mask), m_chr_source); + chr1_2(extra_chr_base | (m_mmc_vrom_bank[2 ^ chr_mirror_mode] & extra_chr_mask), m_chr_source); + chr1_3(extra_chr_base | (m_mmc_vrom_bank[3 ^ chr_mirror_mode] & extra_chr_mask), m_chr_source); + chr1_4(extra_chr_base | (m_mmc_vrom_bank[4] & extra_chr_mask), m_chr_source); + chr1_5(extra_chr_base | (m_mmc_vrom_bank[5] & extra_chr_mask), m_chr_source); + chr1_6(extra_chr_base | (m_mmc_vrom_bank[6] & extra_chr_mask), m_chr_source); + chr1_7(extra_chr_base | (m_mmc_vrom_bank[7] & extra_chr_mask), m_chr_source); + break; + } +} + +void nes_jy_typea_device::update_mirror_typea() +{ + switch (m_reg[1] & 3) + { + case 0: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 1: set_nt_mirroring(PPU_MIRROR_HORZ); break; + case 2: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 3: set_nt_mirroring(PPU_MIRROR_HIGH); break; + } +} + +void nes_jy_typea_device::update_banks(int reg) +{ + switch (reg & 3) + { + case 0: + update_prg(); + update_chr(); + update_mirror(); + break; + case 1: + update_mirror(); + break; + case 2: + update_mirror(); + break; + case 3: + update_prg(); + update_chr(); + break; + } +} + + +WRITE8_MEMBER(nes_jy_typea_device::write_h) +{ + LOG_MMC(("JY Company write_m, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x0000: + offset &= 3; + data &= 0x3f; + if (m_mmc_prg_bank[offset] != data) + { + m_mmc_prg_bank[offset] = data; + update_prg(); + } + break; + case 0x1000: + offset &= 7; + if ((m_mmc_vrom_bank[offset] & 0xff) != data) + { + m_mmc_vrom_bank[offset] = (m_mmc_vrom_bank[offset] & 0xff00) | data; + update_chr(); + } + break; + case 0x2000: + offset &= 7; + if ((m_mmc_vrom_bank[offset] & 0xff00) != (data << 8)) + { + m_mmc_vrom_bank[offset] = (m_mmc_vrom_bank[offset] & 0x00ff) | (data << 8); + update_chr(); + } + break; + case 0x3000: + if (!(offset & 4)) + { + offset &= 3; + m_mmc_nt_bank[offset] = (m_mmc_nt_bank[offset] & 0xff00) | data; + } + else + { + offset &= 3; + m_mmc_nt_bank[offset] = (m_mmc_nt_bank[offset] & 0x00ff) | data << 8; + } + update_mirror(); + break; + case 0x4000: + switch (offset & 7) + { + case 0: + if (BIT(data, 0)) + m_irq_enable = 1; + else + { + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + m_irq_enable = 0; + } + break; + case 1: + m_irq_mode = data & 3; + m_irq_prescale_mask = (data & 4) ? 0x07 : 0xff; + m_irq_down = data & 0x80; + m_irq_up = data & 0x40; + if (m_irq_mode == 0) + irq_timer->adjust(attotime::zero, 0, timer_freq); + else + irq_timer->adjust(attotime::never); + break; + case 2: + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + m_irq_enable = 0; + break; + case 3: + m_irq_enable = 1; + break; + case 4: + m_irq_prescale = data ^ m_irq_flip; + break; + case 5: + m_irq_count = data ^ m_irq_flip; + break; + case 6: + m_irq_flip = data; + break; + case 7: + // this is used for the 'funky' IRQ mode, not implemented yet + break; + } + break; + case 0x5000: + if (m_reg[offset & 3] != data) + { + m_reg[offset & 3] = data; + update_banks(offset & 3); + } + break; + } +} + +/*------------------------------------------------- + + JY Company Type B board emulation + + iNES: mapper 211 + + The mirroring system is a lot more complex in this + board + + -------------------------------------------------*/ + +void nes_jy_typeb_device::update_mirror_typeb() +{ + for (int i = 0; i < 4; i++) + { + if (BIT(m_reg[0], 6)) // CHRROM + set_nt_page(i, VROM, m_mmc_nt_bank[i], 0); + else // might be either CHRROM or CIRAM + { + // CHRROM is only used if bit 7 of the NT Reg does not match bit7 of reg[2]. + if ((m_mmc_nt_bank[i] ^ m_reg[2]) & 0x80) + set_nt_page(i, VROM, m_mmc_nt_bank[i], 0); + else + set_nt_page(i, CIRAM, m_mmc_nt_bank[i] & 1, 1); + } + } +} + +/*------------------------------------------------- + + JY Company Type C board emulation + + iNES: mapper 209 + + These board can switch between the Type A and the + Type B mirroring + + -------------------------------------------------*/ + +void nes_jy_typec_device::update_mirror_typec() +{ + if (BIT(m_reg[0], 5)) + update_mirror_typeb(); + else + update_mirror_typea(); +} + +READ8_MEMBER(nes_jy_typec_device::chr_r) +{ + int bank = offset >> 10; + + irq_clock(0, 2); + switch (offset & 0xff0) + { + case 0xfd0: + m_chr_latch[BIT(offset, 12)] = (bank & 0x4); + update_chr(); + break; + case 0xfe0: + m_chr_latch[BIT(offset, 12)] = (bank & 0x4) | 0x2; + update_chr(); + break; + } + return m_chr_access[bank][offset & 0x3ff]; +} diff --git a/src/devices/bus/nes/jy.h b/src/devices/bus/nes/jy.h new file mode 100644 index 00000000000..f2441654d57 --- /dev/null +++ b/src/devices/bus/nes/jy.h @@ -0,0 +1,104 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_JY_H +#define __NES_JY_H + +#include "nxrom.h" + + +// ======================> nes_jy_typea_device + +class nes_jy_typea_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_jy_typea_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_jy_typea_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual DECLARE_READ8_MEMBER(chr_r); + virtual DECLARE_READ8_MEMBER(nt_r); + + virtual void scanline_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +protected: + void irq_clock(int blanked, int mode); + void update_banks(int reg); + void update_prg(); + void update_chr(); + void update_mirror_typea(); + virtual void update_mirror() { update_mirror_typea(); } + inline UINT8 unscramble(UINT8 bank); + + UINT8 m_mul[2]; + UINT8 m_latch; + UINT8 m_reg[4]; + UINT8 m_chr_latch[2]; // type C uses a more complex CHR 4K mode, and these vars are only changed for those games + UINT8 m_mmc_prg_bank[4]; + UINT16 m_mmc_nt_bank[4]; + UINT16 m_mmc_vrom_bank[8]; + UINT16 m_extra_chr_bank; + UINT16 m_extra_chr_mask; + int m_bank_6000; + + UINT8 m_irq_mode; + UINT8 m_irq_count; + UINT8 m_irq_prescale; + UINT8 m_irq_prescale_mask; + UINT8 m_irq_flip; + int m_irq_enable; + int m_irq_up, m_irq_down; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; + attotime timer_freq; +}; + + +// ======================> nes_jy_typeb_device + +class nes_jy_typeb_device : public nes_jy_typea_device +{ +public: + // construction/destruction + nes_jy_typeb_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_jy_typeb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + +protected: + void update_mirror_typeb(); + virtual void update_mirror() { update_mirror_typeb(); } +}; + +// ======================> nes_jy_typec_device + +class nes_jy_typec_device : public nes_jy_typeb_device +{ +public: + // construction/destruction + nes_jy_typec_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual DECLARE_READ8_MEMBER(chr_r); + +protected: + void update_mirror_typec(); + virtual void update_mirror() { update_mirror_typec(); } +}; + + + + + +// device type definition +extern const device_type NES_JY_TYPEA; +extern const device_type NES_JY_TYPEB; +extern const device_type NES_JY_TYPEC; + +#endif diff --git a/src/devices/bus/nes/kaiser.c b/src/devices/bus/nes/kaiser.c new file mode 100644 index 00000000000..1c7d7bced0a --- /dev/null +++ b/src/devices/bus/nes/kaiser.c @@ -0,0 +1,588 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Kaiser PCBs + + + Here we emulate the following Kaiser bootleg PCBs + + * Kaiser KS202 + * Kaiser KS7012 + * Kaiser KS7013B + * Kaiser KS7017 + * Kaiser KS7022 + * Kaiser KS7032 + * Kaiser KS7058 + + The Kaiser KS7057 bootleg board is emulated in nes_mmc3_clones.c + + + TODO: + - FCEUmm lists more Kaiser PCBs: + * KS7030 (for Yume Koujou Doki Doki Panic by Kaiser?) + * KS7037 + but there seem to be no available dumps... + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "kaiser.h" + +#include "cpu/m6502/m6502.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_KS7058 = &device_creator; +const device_type NES_KS7022 = &device_creator; +const device_type NES_KS7032 = &device_creator; +const device_type NES_KS202 = &device_creator; +const device_type NES_KS7017 = &device_creator; +const device_type NES_KS7012 = &device_creator; +const device_type NES_KS7013B = &device_creator; +const device_type NES_KS7031 = &device_creator; + + +nes_ks7058_device::nes_ks7058_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_KS7058, "NES Cart Kaiser KS-7058 PCB", tag, owner, clock, "nes_ks7058", __FILE__) +{ +} + +nes_ks7022_device::nes_ks7022_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_KS7022, "NES Cart Kaiser KS-7022 PCB", tag, owner, clock, "nes_ks7022", __FILE__) +{ +} + +nes_ks7032_device::nes_ks7032_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_ks7032_device::nes_ks7032_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_KS7032, "NES Cart Kaiser KS-7032 PCB", tag, owner, clock, "nes_ks7032", __FILE__) +{ +} + +nes_ks202_device::nes_ks202_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_ks7032_device(mconfig, NES_KS202, "NES Cart Kaiser KS-202 PCB", tag, owner, clock, "nes_ks202", __FILE__) +{ +} + +nes_ks7017_device::nes_ks7017_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_KS7017, "NES Cart Kaiser KS-7017 PCB", tag, owner, clock, "nes_ks7017", __FILE__) +{ +} + +nes_ks7012_device::nes_ks7012_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_KS7012, "NES Cart Kaiser KS-7012 PCB", tag, owner, clock, "nes_ks7012", __FILE__) +{ +} + +nes_ks7013b_device::nes_ks7013b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_KS7013B, "NES Cart Kaiser KS-7013B PCB", tag, owner, clock, "nes_ks7013b", __FILE__) +{ +} + +nes_ks7031_device::nes_ks7031_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_KS7031, "NES Cart Kaiser KS-7031 PCB", tag, owner, clock, "nes_ks7031", __FILE__) +{ +} + + + + +void nes_ks7058_device::device_start() +{ + common_start(); +} + +void nes_ks7058_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_ks7022_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_ks7022_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; +} + +void nes_ks7032_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_latch)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_reg)); +} + +void nes_ks7032_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; + m_irq_enable = 0; + m_irq_count = 0; + memset(m_reg, 0, sizeof(m_reg)); + prg_update(); +} + +void nes_ks7017_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_latch)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_status)); +} + +void nes_ks7017_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(2); + chr8(0, m_chr_source); + + m_latch = 0; + m_irq_enable = 0; + m_irq_count = 0; + m_irq_status = 0; +} + +void nes_ks7012_device::device_start() +{ + common_start(); +} + +void nes_ks7012_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0xff); + chr8(0, m_chr_source); +} + +void nes_ks7013b_device::device_start() +{ + common_start(); +} + +void nes_ks7013b_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); +} + +void nes_ks7031_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_ks7031_device::pcb_reset() +{ + prg32(0); // not really used... + chr8(0, CHRRAM); + + m_reg[0] = 0; + m_reg[1] = 0; + m_reg[2] = 0; + m_reg[3] = 0; +} + + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Kaiser Board KS7058 + + Games: Tui Do Woo Ma Jeung + + Writes to 0xf000-0xffff set 4k chr banks. Namely, if + offset&0x80 is 0 the lower 4k are set, if it is 1 the + upper 4k are set. + + iNES: mapper 171 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_ks7058_device::write_h) +{ + LOG_MMC(("ks7058 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7080) + { + case 0x7000: + chr4_0(data, CHRROM); + break; + case 0x7080: + chr4_4(data, CHRROM); + break; + } +} + +/*------------------------------------------------- + + Kaiser Board KS7022 + + Games: 15 in 1 + + iNES: mapper 175 + + In MESS: Supported? + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_ks7022_device::write_h) +{ + LOG_MMC(("ks7022 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset == 0) + set_nt_mirroring(BIT(data, 2) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + if (offset == 0x2000) + m_latch = data & 0x0f; +} + +READ8_MEMBER(nes_ks7022_device::read_h) +{ + LOG_MMC(("ks7022 read_h, offset: %04x\n", offset)); + + if (offset == 0x7ffc) + { + chr8(m_latch, CHRROM); + prg16_89ab(m_latch); + prg16_cdef(m_latch); + } + + return hi_access_rom(offset); +} + +/*------------------------------------------------- + + Kaiser Board KS7032 + + Games: A few FDS conversions like Bubble Bobble + or SMB2 + + iNES: + + TODO: available dumps do not seem to use WRAM... + yet m_reg[4] should switch WRAM bank... investigate! + + In MESS: Supported? + + -------------------------------------------------*/ + +void nes_ks7032_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (m_irq_count == 0xffff) + { + m_irq_enable = 0; + m_irq_count = 0; + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + } + else + m_irq_count++; + } + } +} + +void nes_ks7032_device::prg_update() +{ + prg8_89(m_reg[1]); + prg8_ab(m_reg[2]); + prg8_cd(m_reg[3]); +} + +WRITE8_MEMBER(nes_ks7032_device::ks7032_write) +{ + LOG_MMC(("ks7032_write, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x0000: + m_irq_count = (m_irq_count & 0xfff0) | (data & 0x0f); + break; + case 0x1000: + m_irq_count = (m_irq_count & 0xff0f) | ((data & 0x0f) << 4); + break; + case 0x2000: + m_irq_count = (m_irq_count & 0xf0ff) | ((data & 0x0f) << 8); + break; + case 0x3000: + m_irq_count = (m_irq_count & 0x0fff) | ((data & 0x0f) << 12); + break; + case 0x4000: + m_irq_enable = 1; + break; + case 0x6000: + m_latch = data & 0x07; + break; + case 0x7000: + m_reg[m_latch] = data; + prg_update(); + break; + } +} + +READ8_MEMBER(nes_ks7032_device::read_m) +{ + LOG_MMC(("ks7032 read_m, offset: %04x\n", offset)); + return m_prg[((m_reg[4] * 0x2000) + (offset & 0x1fff)) & (m_prg_size - 1)]; +} + +/*------------------------------------------------- + + Kaiser Board KS202 + + Games: Super Mario Bros. 3 (Pirate, Alt) + + iNES: + + In MESS: Supported? + + -------------------------------------------------*/ + + +WRITE8_MEMBER(nes_ks202_device::write_h) +{ + LOG_MMC(("ks202 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x7000: + m_reg[m_latch] = data; + prg_update(); + switch (offset & 0xc00) + { + case 0x800: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ); + break; + case 0xc00: + chr1_x(offset & 0x07, data, CHRROM); + break; + } + break; + default: + ks7032_write(space, offset, data, mem_mask); + break; + } +} + +READ8_MEMBER(nes_ks202_device::read_m) +{ + LOG_MMC(("ks202 read_m, offset: %04x\n", offset)); + return m_prgram[offset & 0x1fff]; +} + +/*------------------------------------------------- + + Kaiser Board KS7017 + + Games: Almana no Kiseki FDS conversion + + iNES: + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_ks7017_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (!m_irq_count) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + m_irq_enable = 0; + m_irq_status |= 0x01; + } + else + m_irq_count--; + } + } +} + +WRITE8_MEMBER(nes_ks7017_device::write_l) +{ + LOG_MMC(("ks7017 write_l, offset: %04x, data: %02x\n", offset, data)); + + offset += 0x100; + + if (offset >= 0xa00 && offset < 0xb00) + m_latch = ((offset >> 2) & 0x03) | ((offset >> 4) & 0x04); + + if (offset >= 0x1100 && offset < 0x1200) + prg16_89ab(m_latch); +} + +WRITE8_MEMBER(nes_ks7017_device::write_ex) +{ + LOG_MMC(("ks7017 write_ex, offset: %04x, data: %02x\n", offset, data)); + offset += 0x20; + + if (offset == 0x0020) // 0x4020 + m_irq_count = (m_irq_count & 0xff00) | data; + + if (offset == 0x0021) // 0x4021 + { + m_irq_count = (m_irq_count & 0x00ff) | (data << 8); + m_irq_enable = 1; + } + + if (offset == 0x0025) // 0x4025 + set_nt_mirroring(BIT(data, 3) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +READ8_MEMBER(nes_ks7017_device::read_ex) +{ + LOG_MMC(("ks7017 read_ex, offset: %04x\n", offset)); + offset += 0x20; + + if (offset == 0x0030) // 0x4030 + { + int temp = m_irq_status; + m_irq_status &= ~0x01; + return temp; + } + + return m_open_bus; // open bus +} + +/*------------------------------------------------- + + Kaiser Board KS7012 + + Games: Zanac FDS Conversion + + iNES: + + In MESS: Not working + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_ks7012_device::write_h) +{ + LOG_MMC(("ks7012 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset == 0x60a0) + prg32(0); + if (offset == 0x6e36) + prg32(1); +} + + +/*------------------------------------------------- + + Kaiser Board KS7013B + + Games: Highway Star FDS Conversion + + iNES: + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_ks7013b_device::write_m) +{ + LOG_MMC(("ks7013b write_l, offset: %04x, data: %02x\n", offset, data)); + prg16_89ab(data); +} + +WRITE8_MEMBER(nes_ks7013b_device::write_h) +{ + LOG_MMC(("ks7013b write_h, offset: %04x, data: %02x\n", offset, data)); + set_nt_mirroring((data & 1) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + + +/*------------------------------------------------- + + Kaiser Board KS7031 + + Games: Dracula II FDS Conversion + + This board is quite weird. It handles 2K PRG chunks + and the chip contains chunks in reverse order, so + that the first 2K are actually loaded at the top + of the 0x8000-0xffff region. Main bank is fixed, while + the 8K mapped at 0x6000-0x7fff varies with reg writes. + + TODO: understand how SRAM is handled... + + iNES: + + In MESS: Supported. + + -------------------------------------------------*/ + +READ8_MEMBER(nes_ks7031_device::read_m) +{ +// LOG_MMC(("ks7031 read_m, offset: %04x\n", offset)); + return m_prg[(m_reg[(offset >> 11) & 3] * 0x0800) + (offset & 0x7ff)]; +} + +READ8_MEMBER(nes_ks7031_device::read_h) +{ + // here the first 32K are accessed, but in 16x2K blocks loaded in reverse order + int accessed_2k = (offset >> 11) & 0x0f; + return m_prg[((0x0f - accessed_2k) * 0x0800) + (offset & 0x7ff)]; +} + +WRITE8_MEMBER(nes_ks7031_device::write_h) +{ + LOG_MMC(("ks7031 write_h, offset: %04x, data: %02x\n", offset, data)); + m_reg[(offset >> 11) & 3] = data & 0x3f; +} diff --git a/src/devices/bus/nes/kaiser.h b/src/devices/bus/nes/kaiser.h new file mode 100644 index 00000000000..b00f410ba05 --- /dev/null +++ b/src/devices/bus/nes/kaiser.h @@ -0,0 +1,184 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_KAISER_H +#define __NES_KAISER_H + +#include "nxrom.h" + + +// ======================> nes_ks7058_device + +class nes_ks7058_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ks7058_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_ks7022_device + +class nes_ks7022_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ks7022_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_ks7032_device + +class nes_ks7032_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ks7032_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_ks7032_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(ks7032_write); + virtual DECLARE_WRITE8_MEMBER(write_h) { ks7032_write(space, offset, data, mem_mask); } + + virtual void pcb_reset(); + +protected: + void prg_update(); + + UINT8 m_latch; + UINT8 m_reg[8]; + + UINT16 m_irq_count; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; +}; + + +// ======================> nes_ks202_device + +class nes_ks202_device : public nes_ks7032_device +{ +public: + // construction/destruction + nes_ks202_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_h); +}; + + +// ======================> nes_ks7017_device + +class nes_ks7017_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ks7017_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(read_ex); + virtual DECLARE_WRITE8_MEMBER(write_ex); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; + + UINT16 m_irq_count; + UINT8 m_irq_status; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; +}; + + +// ======================> nes_ks7012_device + +class nes_ks7012_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ks7012_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + +// ======================> nes_ks7013b_device + +class nes_ks7013b_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ks7013b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_ks7031_device + +class nes_ks7031_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ks7031_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[4]; +}; + + + +// device type definition +extern const device_type NES_KS7058; +extern const device_type NES_KS7022; +extern const device_type NES_KS7032; +extern const device_type NES_KS202; +extern const device_type NES_KS7017; +extern const device_type NES_KS7012; +extern const device_type NES_KS7013B; +extern const device_type NES_KS7031; + +#endif diff --git a/src/devices/bus/nes/karastudio.c b/src/devices/bus/nes/karastudio.c new file mode 100644 index 00000000000..002bdf6eea3 --- /dev/null +++ b/src/devices/bus/nes/karastudio.c @@ -0,0 +1,309 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Bandai Karaoke Studio + + + Here we emulate the following PCBs Bandai Karaoke Studio [mapper 188] + + The Karaoke Studio cart consist of a large connector which fits the FC cart slot, with a microphone + connected. The game data is in the connector itself. The microphone has two buttons on it, and the + game uses these only to navigate through the menus (the two buttons are not read through the controller + port, which is not accessible from the cart, but from $6000-$7fff). Part of the connector body can be + removed to be replaced by an expansion cart containing new songs (we emulate this by adding a -cart2 slot). + + + TODO: + - verify expansion slot emulation for the Senyou Cassettes: + not much documentation exists about the expansion carts (except for few paragraphs + at Enri's FC webpage), so I implemented it based on "common sense" + * expansion carts do not contain the required game data => main PRG must be in the main cart + so to remain connected even when an expansion is inserted (differently from Datach, where + the base unit contains no PRG) + * bankswicth writes with bit3=0 (to access expansion) when no expansion is present should do + nothing + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "karastudio.h" + +#ifdef NES_PCB_DEBUG +#define VERBOSE 1 +#else +#define VERBOSE 0 +#endif + +#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0) + + +//----------------------------------------- +// +// Karaoke Studio Cartslot implementation +// +//----------------------------------------- + +//------------------------------------------------- +// sub-cart interface +//------------------------------------------------- + +kstudio_cart_interface::kstudio_cart_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig, device), + m_rom(NULL) +{ +} + +kstudio_cart_interface::~kstudio_cart_interface() +{ +} + +READ8_MEMBER(kstudio_cart_interface::read) +{ + return m_rom[(m_bank * 0x4000) + (offset & 0x3fff)]; +} + +//------------------------------------------------- +// sub-cart slot device +//------------------------------------------------- + +const device_type NES_KSEXPANSION_SLOT = &device_creator; + +nes_kstudio_slot_device::nes_kstudio_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, NES_KSEXPANSION_SLOT, "NES Karaoke Studio Expansion Slot", tag, owner, clock, "nes_ks_slot", __FILE__), + device_image_interface(mconfig, *this), + device_slot_interface(mconfig, *this) +{ +} + +nes_kstudio_slot_device::~nes_kstudio_slot_device() +{ +} + + +void nes_kstudio_slot_device::device_start() +{ + m_cart = dynamic_cast(get_card_device()); +} + +READ8_MEMBER(nes_kstudio_slot_device::read) +{ + if (m_cart) + return m_cart->read(space, offset, mem_mask); + + return 0xff; +} + +bool nes_kstudio_slot_device::call_load() +{ + if (m_cart) + { + UINT8 *ROM = m_cart->get_cart_base(); + + if (!ROM) + return IMAGE_INIT_FAIL; + + // Existing exapnsion carts are all 128K, so we only load files of this size + if (software_entry() == NULL) + { + if (length() != 0x20000) + return IMAGE_INIT_FAIL; + + fread(&ROM, 0x20000); + } + else + { + if (get_software_region_length("rom") != 0x20000) + return IMAGE_INIT_FAIL; + + memcpy(ROM, get_software_region("rom"), 0x20000); + } + } + + return IMAGE_INIT_PASS; +} + + +bool nes_kstudio_slot_device::call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry) +{ + load_software_part_region(*this, swlist, swname, start_entry ); + return TRUE; +} + +void nes_kstudio_slot_device::get_default_card_software(std::string &result) +{ + software_get_default_slot(result, "ks_exp"); +} + + +//----------------------------------------------- +// +// Karaoke Studio Expansion cart implementation +// +//----------------------------------------------- + +ROM_START( ks_exp_rom ) + ROM_REGION(0x20000, "exrom", ROMREGION_ERASEFF) +ROM_END + +const device_type NES_KSEXPANSION_ROM = &device_creator; + +nes_kstudio_rom_device::nes_kstudio_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, NES_KSEXPANSION_ROM, "NES Karaoke Studio Expansion ROM", tag, owner, clock, "nes_ks_rom", __FILE__), + kstudio_cart_interface( mconfig, *this ) +{ +} + +void nes_kstudio_rom_device::device_start() +{ + m_rom = (UINT8*)memregion("exrom")->base(); + save_item(NAME(m_bank)); +} + +void nes_kstudio_rom_device::device_reset() +{ + m_bank = 0; +} + +const rom_entry *nes_kstudio_rom_device::device_rom_region() const +{ + return ROM_NAME( ks_exp_rom ); +} + +UINT8 *nes_kstudio_rom_device::get_cart_base() +{ + return m_rom; +} + + +//------------------------------------------ +// +// Karaoke Studio Base Cart implementation +// +//------------------------------------------ + +const device_type NES_KARAOKESTUDIO = &device_creator; + + +nes_karaokestudio_device::nes_karaokestudio_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_KARAOKESTUDIO, "NES Cart Bandai Karaoke Studio PCB", tag, owner, clock, "nes_karaoke", __FILE__), + m_subslot(*this, "exp_slot"), + m_mic_ipt(*this, "MIC") +{ +} + + +void nes_karaokestudio_device::device_start() +{ + common_start(); + save_item(NAME(m_exp_active)); +} + +void nes_karaokestudio_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef((m_prg_chunks - 1) ^ 0x08); + chr8(0, m_chr_source); + m_exp_active = 0; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Bandai Karaoke Studio board emulation + + Games: Karaoke Studio + expansion carts with + additional songs + + Note: we currently do not emulate properly the + expansion slot + + iNES: mapper 188 + + -------------------------------------------------*/ + +READ8_MEMBER(nes_karaokestudio_device::read_m) +{ + LOG_MMC(("karaoke studio read_m, offset: %04x\n", offset)); + return m_mic_ipt->read(); +} + +READ8_MEMBER(nes_karaokestudio_device::read_h) +{ + LOG_MMC(("karaoke studio read_h, offset: %04x\n", offset)); + // this shall be the proper code, but it's a bit slower, so we access directly the subcart below + //return m_subslot->read(space, offset, mem_mask); + + // access expansion cart only if all of the followings are verified + // * we are in $8000-$bfff range + // * there has been a bankswitch write to map the expansion to such range + // * there actually is an expansion cart mounted + if (offset < 0x4000 && m_exp_active && m_subslot->m_cart) + return m_subslot->m_cart->read(space, offset, mem_mask); + else + return hi_access_rom(offset); +} + +WRITE8_MEMBER(nes_karaokestudio_device::write_h) +{ + LOG_MMC(("karaoke studio write_h, offset: %04x, data: %02x\n", offset, data)); + // bit3 1 = M ROM (main unit), 0=E ROM (expansion) + // HACK(?): currently it is not clear how the unit acknowledges the presence of the expansion + // cart (when expansion is present, code keeps switching both from the expansion rom and from + // the main ROM) + // my guess is that writes with bit3=0 and no expansion just do nothing, but it shall be verified + + if (offset >= 04000) + { + if (BIT(data, 3)) + { + m_exp_active = 0; + prg16_89ab(data & 7); + } + else // expansion cart + { + m_exp_active = 1; + m_subslot->write_prg_bank(data & 7); + } + } +} + + +static INPUT_PORTS_START( karaoke_mic ) + PORT_START("MIC") + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("A (Mic Select)") PORT_CODE(KEYCODE_X) + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("B (Mic Start)") PORT_CODE(KEYCODE_Z) + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Microphone (?)") PORT_CODE(KEYCODE_C) + PORT_BIT( 0xf8, IP_ACTIVE_HIGH, IPT_UNUSED ) +INPUT_PORTS_END + +ioport_constructor nes_karaokestudio_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( karaoke_mic ); +} + + +//------------------------------------------------- +// CART SLOT +//------------------------------------------------- + +static SLOT_INTERFACE_START(karaoke_studio_cart) + SLOT_INTERFACE_INTERNAL("ks_exp", NES_KSEXPANSION_ROM) +SLOT_INTERFACE_END + + +MACHINE_CONFIG_FRAGMENT( karaoke_studio ) + MCFG_KSTUDIO_MINICART_ADD("exp_slot", karaoke_studio_cart) +MACHINE_CONFIG_END + +machine_config_constructor nes_karaokestudio_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( karaoke_studio ); +} diff --git a/src/devices/bus/nes/karastudio.h b/src/devices/bus/nes/karastudio.h new file mode 100644 index 00000000000..b0b671e0034 --- /dev/null +++ b/src/devices/bus/nes/karastudio.h @@ -0,0 +1,147 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_KARASTUDIO_H +#define __NES_KARASTUDIO_H + +#include "nxrom.h" + + +//----------------------------------------- +// +// Karaoke Studio Cartslot implementation +// +//----------------------------------------- + +// ======================> kstudio_cart_interface + +class kstudio_cart_interface : public device_slot_card_interface +{ +public: + // construction/destruction + kstudio_cart_interface(const machine_config &mconfig, device_t &device); + virtual ~kstudio_cart_interface(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read); + + UINT8 *get_cart_base() { return m_rom; } + void write_prg_bank(UINT8 bank) { m_bank = bank; } + +protected: + // internal state + UINT8 *m_rom; + // ROM is accessed via two 16K banks, but only the first one can be switched + UINT8 m_bank; +}; + +// ======================> nes_kstudio_slot_device + +class nes_kstudio_slot_device : public device_t, + public device_image_interface, + public device_slot_interface +{ +public: + // construction/destruction + nes_kstudio_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual ~nes_kstudio_slot_device(); + + // device-level overrides + virtual void device_start(); + virtual void device_config_complete() { update_names(); } + + // image-level overrides + virtual bool call_load(); + virtual bool call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry); + + virtual iodevice_t image_type() const { return IO_CARTSLOT; } + virtual bool is_readable() const { return 1; } + virtual bool is_writeable() const { return 0; } + virtual bool is_creatable() const { return 0; } + virtual bool must_be_loaded() const { return 0; } + virtual bool is_reset_on_load() const { return 1; } + virtual const char *image_interface() const { return "ks_cart"; } + virtual const char *file_extensions() const { return "bin"; } + virtual const option_guide *create_option_guide() const { return NULL; } + + // slot interface overrides + virtual void get_default_card_software(std::string &result); + + virtual DECLARE_READ8_MEMBER(read); + void write_prg_bank(UINT8 bank) { if (m_cart) m_cart->write_prg_bank(bank); } + + kstudio_cart_interface* m_cart; +}; + +// device type definition +extern const device_type NES_KSEXPANSION_SLOT; + + +#define MCFG_KSTUDIO_MINICART_ADD(_tag, _slot_intf) \ + MCFG_DEVICE_ADD(_tag, NES_KSEXPANSION_SLOT, 0) \ +MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, NULL, false) + + +//----------------------------------------------- +// +// Karaoke Studio Expansion cart implementation +// +//----------------------------------------------- + +// ======================> nes_kstudio_rom_device + +class nes_kstudio_rom_device : public device_t, + public kstudio_cart_interface +{ +public: + // construction/destruction + nes_kstudio_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // optional information overrides + virtual const rom_entry *device_rom_region() const; + virtual UINT8* get_cart_base(); + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); +}; + +// device type definition +extern const device_type NES_KSEXPANSION_ROM; + + +//------------------------------------------- +// +// Karaoke Studio Base Cart implementation +// +//------------------------------------------- + +// ======================> nes_karaokestudio_device + +class nes_karaokestudio_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_karaokestudio_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual ioport_constructor device_input_ports() const; + virtual machine_config_constructor device_mconfig_additions() const; + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + int m_exp_active; + required_device m_subslot; + required_ioport m_mic_ipt; +}; + + +// device type definition +extern const device_type NES_KARAOKESTUDIO; + +#endif diff --git a/src/devices/bus/nes/konami.c b/src/devices/bus/nes/konami.c new file mode 100644 index 00000000000..4cd4f1fd872 --- /dev/null +++ b/src/devices/bus/nes/konami.c @@ -0,0 +1,787 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Konami PCBs + + + 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 "konami.h" + +#include "cpu/m6502/m6502.h" + +#ifdef NES_PCB_DEBUG +#define VERBOSE 1 +#else +#define VERBOSE 0 +#endif + +#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0) + +#define N2A03_DEFAULTCLOCK (21477272.724 / 12) + +//------------------------------------------------- +// constructor +//------------------------------------------------- + +const device_type NES_VRC1 = &device_creator; +const device_type NES_VRC2 = &device_creator; +const device_type NES_VRC3 = &device_creator; +const device_type NES_VRC4 = &device_creator; +const device_type NES_VRC6 = &device_creator; +const device_type NES_VRC7 = &device_creator; + + +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, 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, 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_vrc4_device::nes_konami_vrc4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_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_vrc4_device(mconfig, NES_VRC6, "NES Cart Konami VRC-6 PCB", tag, owner, clock, "nes_vrc6", __FILE__), + m_vrc6snd(*this, "vrc6snd") +{ +} + +nes_konami_vrc7_device::nes_konami_vrc7_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_konami_vrc4_device(mconfig, NES_VRC7, "NES Cart Konami VRC-7 PCB", tag, owner, clock, "nes_vrc7", __FILE__), + m_ym2413(*this, "ym") +{ +} + + + +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(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + 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)); +} + +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_mode = 0; + 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(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_mode)); + save_item(NAME(m_irq_prescale)); + 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_prescale = 341; + 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_vrc6_device::device_start() +{ + nes_konami_vrc4_device::device_start(); +} + +void nes_konami_vrc7_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_mode)); + save_item(NAME(m_irq_prescale)); + 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_mode = 0; + m_irq_prescale = 341; + 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.empty()) + return m_battery[offset & (m_battery.size() - 1)]; + else if (!m_prgram.empty()) + 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) ? ((m_open_bus & 0xfe) | (m_latch & 1)) : m_open_bus; +} + +WRITE8_MEMBER(nes_konami_vrc2_device::write_m) +{ + LOG_MMC(("VRC-2 write_m, offset: %04x, data: %02x\n", offset, data)); + + if (!m_battery.empty()) + m_battery[offset & (m_battery.size() - 1)] = data; + else if (!m_prgram.empty()) + 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. + + -------------------------------------------------*/ + +void nes_konami_vrc3_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (m_irq_mode) // 8bits mode + { + if ((m_irq_count & 0x00ff) == 0xff) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + m_irq_count = m_irq_count_latch; + } + else + m_irq_count = (m_irq_count & 0xff00) | ((m_irq_count & 0x00ff) + 1); + } + else // 16bits mode + { + if (m_irq_count == 0xffff) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + m_irq_count = m_irq_count_latch; + } + else + m_irq_count++; + } + } + } +} + +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: + m_irq_count_latch = (m_irq_count_latch & 0xfff0) | ((data & 0x0f) << 0); + break; + case 0x1000: + m_irq_count_latch = (m_irq_count_latch & 0xff0f) | ((data & 0x0f) << 4); + break; + case 0x2000: + m_irq_count_latch = (m_irq_count_latch & 0xf0ff) | ((data & 0x0f) << 8); + break; + case 0x3000: + m_irq_count_latch = (m_irq_count_latch & 0x0fff) | ((data & 0x0f) << 12); + break; + case 0x4000: + m_irq_mode = data & 0x04; + m_irq_enable = data & 0x02; + m_irq_enable_latch = data & 0x01; + if (data & 0x02) + m_irq_count = m_irq_count_latch; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x5000: + m_irq_enable = m_irq_enable_latch; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + 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::irq_tick() +{ + if (m_irq_count == 0xff) + { + m_irq_count = m_irq_count_latch; + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + } + else + m_irq_count++; +} + +void nes_konami_vrc4_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (m_irq_mode) // cycle mode + irq_tick(); + else // scanline mode + { + // A prescaler divides the passing CPU cycles by 114, 114, then 113 (and repeats that order). + // This approximates 113+2/3 CPU cycles, which is one NTSC scanline. + // Since this is a CPU-based IRQ, though, it is triggered also during non visible scanlines... + if (m_irq_prescale < 3) + { + m_irq_prescale += 341; + irq_tick(); + } + + m_irq_prescale -= 3; + } + } + } +} + +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; + m_irq_enable = data & 0x02; + m_irq_enable_latch = data & 0x01; + if (data & 0x02) + m_irq_count = m_irq_count_latch; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x300: + m_irq_enable = m_irq_enable_latch; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + 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: // pulse 1 & global control + m_vrc6snd->write(space, add_lines>>8, data); + break; + case 0x2000: // pulse 2 + m_vrc6snd->write(space, (add_lines>>8) | 0x100, 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 // saw + m_vrc6snd->write(space, (add_lines>>8) | 0x200, 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; + m_irq_enable = data & 0x02; + m_irq_enable_latch = data & 0x01; + if (data & 0x02) + m_irq_count = m_irq_count_latch; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x200: + m_irq_enable = m_irq_enable_latch; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + 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; + } +} + +static MACHINE_CONFIG_FRAGMENT( vrc6 ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("vrc6snd", VRC6, N2A03_DEFAULTCLOCK) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.5) +MACHINE_CONFIG_END + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor nes_konami_vrc6_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( vrc6 ); +} + +/*------------------------------------------------- + + 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: + m_ym2413->register_port_w(space, 0, data); + break; + case 0x1030: + case 0x1038: + m_ym2413->data_port_w(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; + m_irq_enable = data & 0x02; + m_irq_enable_latch = data & 0x01; + if (data & 0x02) + m_irq_count = m_irq_count_latch; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x7008: case 0x7010: case 0x7018: + m_irq_enable = m_irq_enable_latch; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + 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! + +static MACHINE_CONFIG_FRAGMENT( vrc7 ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("ym", YM2413, N2A03_DEFAULTCLOCK) + 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 ); +} diff --git a/src/devices/bus/nes/konami.h b/src/devices/bus/nes/konami.h new file mode 100644 index 00000000000..0ecece270fd --- /dev/null +++ b/src/devices/bus/nes/konami.h @@ -0,0 +1,155 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_KONAMI_H +#define __NES_KONAMI_H + +#include "nxrom.h" +#include "sound/vrc6.h" +#include "sound/2413intf.h" + + +// ======================> nes_konami_vrc1_device + +class nes_konami_vrc1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_konami_vrc1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_mmc_vrom_bank[2]; +}; + + +// ======================> nes_konami_vrc2_device + +class nes_konami_vrc2_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_konami_vrc2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_mmc_vrom_bank[8]; + UINT8 m_latch; +}; + + +// ======================> nes_konami_vrc3_device + +class nes_konami_vrc3_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_konami_vrc3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT16 m_irq_count, m_irq_count_latch; + int m_irq_enable, m_irq_enable_latch; + int m_irq_mode; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; +}; + + +// ======================> nes_konami_vrc4_device + +class nes_konami_vrc4_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_konami_vrc4_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_konami_vrc4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + void set_prg(); + UINT8 m_mmc_vrom_bank[8]; + UINT8 m_latch, m_mmc_prg_bank; + + void irq_tick(); + UINT16 m_irq_count, m_irq_count_latch; + int m_irq_enable, m_irq_enable_latch; + int m_irq_mode; + int m_irq_prescale; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; +}; + + +// ======================> nes_konami_vrc6_device + +class nes_konami_vrc6_device : public nes_konami_vrc4_device +{ +public: + // construction/destruction + nes_konami_vrc6_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; + virtual DECLARE_WRITE8_MEMBER(write_h); + + required_device m_vrc6snd; +}; + + +// ======================> nes_konami_vrc7_device + +class nes_konami_vrc7_device : public nes_konami_vrc4_device +{ +public: + // construction/destruction + nes_konami_vrc7_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + required_device m_ym2413; +}; + + + +// device type definition +extern const device_type NES_VRC1; +extern const device_type NES_VRC2; +extern const device_type NES_VRC3; +extern const device_type NES_VRC4; +extern const device_type NES_VRC6; +extern const device_type NES_VRC7; + +#endif diff --git a/src/devices/bus/nes/legacy.c b/src/devices/bus/nes/legacy.c new file mode 100644 index 00000000000..3a452fd2f39 --- /dev/null +++ b/src/devices/bus/nes/legacy.c @@ -0,0 +1,311 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for FFE PCBs + + + Here we emulate the mappers used by available Far Front East copier hacked games [mappers 6, 8, 17] + + TODO: + - investigate IRQ mechanism (current code is broken) + - replace this with proper copier emulation, using disk images... + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "legacy.h" + +#include "cpu/m6502/m6502.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_FFE3 = &device_creator; +const device_type NES_FFE4 = &device_creator; +const device_type NES_FFE8 = &device_creator; + + +nes_ffe3_device::nes_ffe3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_FFE3, "NES Cart FFE-3 PCB", tag, owner, clock, "nes_ff3", __FILE__) +{ +} + +nes_ffe4_device::nes_ffe4_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_ffe4_device::nes_ffe4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_FFE4, "NES Cart FFE-4 PCB", tag, owner, clock, "nes_ff4", __FILE__) +{ +} + +nes_ffe8_device::nes_ffe8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_ffe4_device(mconfig, NES_FFE8, "NES Cart FFE-8 PCB", tag, owner, clock, "nes_ff8", __FILE__) +{ +} + + + +void nes_ffe3_device::device_start() +{ + common_start(); +} + +void nes_ffe3_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + + +void nes_ffe4_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_exram)); + save_item(NAME(m_exram_enabled)); + save_item(NAME(m_exram_bank)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_latch)); +} + +void nes_ffe4_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(7); + chr8(0, m_chr_source); + + m_exram_enabled = 0; + m_exram_bank = 0; + + m_latch = 0; + m_irq_enable = 0; + m_irq_count = 0; +} + + +void nes_ffe8_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0xff); + chr8(0, m_chr_source); + + // extra vram is not used by this board, so these will remain always zero + m_exram_enabled = 0; + m_exram_bank = 0; + + m_latch = 0; + m_irq_enable = 0; + m_irq_count = 0; +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Mapper 8 + + Known Boards: FFE3 Copier Board + Games: Hacked versions of games + + In MESS: Supported? (I have no games to test this) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_ffe3_device::write_h) +{ + LOG_MMC(("mapper8 write_h, offset: %04x, data: %02x\n", offset, data)); + + chr8(data & 0x07, CHRROM); + prg16_89ab(data >> 3); +} + +/*------------------------------------------------- + + Mapper 6 + + Known Boards: FFE4 Copier Board + Games: Hacked versions of games + + In MESS: Supported? Not sure if we could also have ExRAM or not... + However, priority is pretty low for this mapper. + + -------------------------------------------------*/ + +void nes_ffe4_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (m_irq_count == 0xffff) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + m_irq_count = 0; + m_irq_enable = 0; + } + else + m_irq_count++; + } + } +} + +WRITE8_MEMBER(nes_ffe4_device::write_l) +{ + LOG_MMC(("mapper6 write_l, offset: %04x, data: %02x\n", offset, data)); + + switch (offset) + { + case 0x1fe: + m_latch = data & 0x80; + set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW); + break; + case 0x1ff: + set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + + case 0x401: + m_irq_enable = 0; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x402: + m_irq_count = (m_irq_count & 0xff00) | data; + break; + case 0x403: + m_irq_enable = 1; + m_irq_count = (m_irq_count & 0x00ff) | (data << 8); + break; + } +} + +WRITE8_MEMBER(nes_ffe4_device::chr_w) +{ + int bank = offset >> 10; + if (m_exram_enabled) + m_exram[(m_exram_bank * 0x2000) + (bank * 0x400) + (offset & 0x3ff)] = data; + + if (m_chr_src[bank] == CHRRAM) + m_chr_access[bank][offset & 0x3ff] = data; +} + +READ8_MEMBER(nes_ffe4_device::chr_r) +{ + int bank = offset >> 10; + if (m_exram_enabled) + return m_exram[(m_exram_bank * 0x2000) + (bank * 0x400) + (offset & 0x3ff)]; + + return m_chr_access[bank][offset & 0x3ff]; +} + + +WRITE8_MEMBER(nes_ffe4_device::write_h) +{ + LOG_MMC(("mapper6 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (!m_latch) // when in "FFE mode" we are forced to use CHRRAM/EXRAM bank? + { + prg16_89ab(data >> 2); + + // This part is not fully documented, so we proceed a bit blindly... + if ((data & 0x03) == 0) + { + m_exram_enabled = 0; + chr8(0, CHRRAM); + } + else + { + m_exram_enabled = 1; + m_exram_bank = data & 0x03; + } + } + else // otherwise, we use CHRROM (shall we check if it's present?) + chr8(data, CHRROM); +} + +/*------------------------------------------------- + + Mapper 17 + + Known Boards: FFE8 Copier Board + Games: Hacked versions of games + + In MESS: Partially Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_ffe8_device::write_l) +{ + LOG_MMC(("mapper17 write_l, offset: %04x, data: %02x\n", offset, data)); + + switch (offset) + { + case 0x1fe: + set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW); + break; + case 0x1ff: + set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + + case 0x401: + m_irq_enable = 0; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x402: + m_irq_count = (m_irq_count & 0xff00) | data; + break; + case 0x403: + m_irq_enable = 1; + m_irq_count = (m_irq_count & 0x00ff) | (data << 8); + break; + + case 0x404: + prg8_89(data); + break; + case 0x405: + prg8_ab(data); + break; + case 0x406: + prg8_cd(data); + break; + case 0x407: + prg8_ef(data); + break; + + case 0x410: + case 0x411: + case 0x412: + case 0x413: + case 0x414: + case 0x415: + case 0x416: + case 0x417: + chr1_x(offset & 7, data, CHRROM); + break; + } +} diff --git a/src/devices/bus/nes/legacy.h b/src/devices/bus/nes/legacy.h new file mode 100644 index 00000000000..4b4667ab61b --- /dev/null +++ b/src/devices/bus/nes/legacy.h @@ -0,0 +1,81 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_LEGACY_H +#define __NES_LEGACY_H + +#include "nxrom.h" + + +// ======================> nes_ffe3_device + +class nes_ffe3_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ffe3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_ffe4_device + +class nes_ffe4_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ffe4_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_ffe4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual DECLARE_READ8_MEMBER(chr_r); + virtual DECLARE_WRITE8_MEMBER(chr_w); + + virtual void pcb_reset(); + +protected: + UINT16 m_irq_count; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; + + UINT8 m_latch; + int m_exram_enabled; + int m_exram_bank; + UINT8 m_exram[0x8000]; +}; + + +// ======================> nes_ffe8_device + +class nes_ffe8_device : public nes_ffe4_device +{ +public: + // construction/destruction + nes_ffe8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h) {} + + virtual void pcb_reset(); +}; + + + +// device type definition +extern const device_type NES_FFE3; +extern const device_type NES_FFE4; +extern const device_type NES_FFE8; + + +#endif diff --git a/src/devices/bus/nes/mmc1.c b/src/devices/bus/nes/mmc1.c new file mode 100644 index 00000000000..ff31250ee19 --- /dev/null +++ b/src/devices/bus/nes/mmc1.c @@ -0,0 +1,374 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Nintendo MMC-1 PCBs + + + Here we emulate the Nintendo SxROM / MMC-1 PCBs + older variants without WRAM protect bit + + Known issues on specific mappers: + + * 001 Yoshi flashes in-game. + * 001 Back to the Future have heavily corrupted graphics (since forever). + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "mmc1.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_SXROM = &device_creator; +const device_type NES_SOROM = &device_creator; +const device_type NES_SXROM_A = &device_creator; +const device_type NES_SOROM_A = &device_creator; + + + +nes_sxrom_device::nes_sxrom_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_sxrom_device::nes_sxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SXROM, "NES Cart SxROM (MMC-1) PCB", tag, owner, clock, "nes_sxrom", __FILE__) +{ +} + +nes_sorom_device::nes_sorom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sxrom_device(mconfig, NES_SOROM, "NES Cart SOROM (MMC-1) PCB", tag, owner, clock, "nes_sorom", __FILE__) +{ +} + +nes_sxrom_a_device::nes_sxrom_a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sxrom_device(mconfig, NES_SXROM_A, "NES Cart SxROM (MMC-1A) PCB", tag, owner, clock, "nes_sxrom_a", __FILE__) +{ +} + +nes_sorom_a_device::nes_sorom_a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sxrom_device(mconfig, NES_SOROM_A, "NES Cart SOROM (MMC-1A) PCB", tag, owner, clock, "nes_sorom_a", __FILE__) +{ +} + + + +void nes_sxrom_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + save_item(NAME(m_count)); + save_item(NAME(m_reg)); + save_item(NAME(m_reg_write_enable)); +} + +void nes_sxrom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_latch = 0; + m_count = 0; + m_reg[0] = 0x0f; + m_reg[1] = m_reg[2] = m_reg[3] = 0; + m_reg_write_enable = 1; + + set_nt_mirroring(PPU_MIRROR_HORZ); + set_chr(); + set_prg(); +} + +void nes_sorom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_latch = 0; + m_count = 0; + m_reg[0] = 0x0f; + m_reg[1] = m_reg[2] = m_reg[3] = 0; + m_reg_write_enable = 1; + + set_nt_mirroring(PPU_MIRROR_HORZ); + set_chr(); + set_prg(); +} + +void nes_sorom_a_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_latch = 0; + m_count = 0; + m_reg[0] = 0x0f; + m_reg[1] = m_reg[2] = m_reg[3] = 0; + m_reg_write_enable = 1; + + set_nt_mirroring(PPU_MIRROR_HORZ); + set_chr(); + set_prg(); +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + + +/*------------------------------------------------- + + SxROM (MMC1 based) board emulation + + iNES: mapper 1 (and 155 for the MMC1A variant which does not + have WRAM disable bit) + + -------------------------------------------------*/ + +TIMER_CALLBACK_MEMBER( nes_sxrom_device::resync_callback ) +{ + m_reg_write_enable = 1; +} + + +void nes_sxrom_device::set_prg() +{ + UINT8 prg_mode, prg_offset; + + prg_mode = m_reg[0] & 0x0c; + /* prg_mode&0x8 determines bank size: 32k (if 0) or 16k (if 1)? when in 16k mode, + prg_mode&0x4 determines which half of the PRG space we can swap: if it is 4, + m_reg[3] sets banks at 0x8000; if it is 0, m_reg[3] sets banks at 0xc000. */ + + prg_offset = m_reg[1] & 0x10; + /* In principle, m_reg[2]&0x10 might affect "extended" banks as well, when chr_mode=1. + However, quoting Disch's docs: When in 4k CHR mode, 0x10 in both $A000 and $C000 *must* be + set to the same value, or else pages will constantly be swapped as graphics render! + Hence, we use only m_reg[1]&0x10 for prg_offset */ + + switch (prg_mode) + { + case 0x00: + case 0x04: +// printf("PRG 32 bank %d \n", (prg_offset + m_reg[3]) >> 1); + prg32((prg_offset + m_reg[3]) >> 1); + break; + case 0x08: +// printf("PRG 16 bank %d (high) \n", prg_offset + m_reg[3]); + prg16_89ab(prg_offset + 0); + prg16_cdef(prg_offset + m_reg[3]); + break; + case 0x0c: +// printf("PRG 16 bank %d (low) \n", prg_offset + m_reg[3]); + prg16_89ab(prg_offset + m_reg[3]); + prg16_cdef(prg_offset + 0x0f); + break; + } +} + +void nes_sxrom_device::set_chr() +{ + UINT8 chr_mode = BIT(m_reg[0], 4); + + if (chr_mode) + { + chr4_0(m_reg[1] & 0x1f, m_chr_source); + chr4_4(m_reg[2] & 0x1f, m_chr_source); + } + else + chr8((m_reg[1] & 0x1f) >> 1, m_chr_source); +} + +// this allows for easier implementation of the NES-EVENT board used for Nintento World Championships +void nes_sxrom_device::update_regs(int reg) +{ + switch (reg) + { + case 0: + switch (m_reg[0] & 0x03) + { + case 0: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 1: set_nt_mirroring(PPU_MIRROR_HIGH); break; + case 2: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 3: set_nt_mirroring(PPU_MIRROR_HORZ); break; + } + set_chr(); + set_prg(); + break; + case 1: + set_chr(); + set_prg(); + break; + case 2: + set_chr(); + break; + case 3: + set_prg(); + break; + } +} + +WRITE8_MEMBER( nes_sxrom_device::write_h ) +{ + LOG_MMC(("sxrom write_h, offset: %04x, data: %02x\n", offset, data)); + + // There is only one latch and shift counter, shared amongst the 4 regs (testcase: Space Shuttle) + + /* here we would need to add an if(cpu_cycles_passed>1) test, and + if requirement is not met simply return without writing anything. + Some games (AD&D Hillsfar, Bill & Ted Excellent Adventure, Cosmic + Wars, Rocket Ranger, Sesame Street 123 and Snow Brothers) rely on + this behavior!! */ + if (!m_reg_write_enable) + return; + else + { + m_reg_write_enable = 0; + machine().scheduler().synchronize(timer_expired_delegate(FUNC(nes_sxrom_device::resync_callback),this)); + } + + if (data & 0x80) + { + m_count = 0; + m_latch = 0; + + // Set reg at 0x8000 to size 16k and lower half swap - needed for Robocop 3, Dynowars + m_reg[0] |= 0x0c; + set_prg(); + return; + } + + if (m_count < 5) + { + if (m_count == 0) m_latch = 0; + m_latch >>= 1; + m_latch |= (data & 0x01) ? 0x10 : 0x00; + m_count++; + } + + if (m_count == 5) + { + m_reg[(offset & 0x6000) >> 13] = m_latch; + update_regs((offset & 0x6000) >> 13); + m_count = 0; + } +} + +WRITE8_MEMBER(nes_sxrom_device::write_m) +{ + UINT8 bank = (m_reg[1] >> 2) & 3; + LOG_MMC(("sxrom write_m, offset: %04x, data: %02x\n", offset, data)); + + if (!BIT(m_reg[3], 4)) // WRAM enabled + { + if (!m_battery.empty()) + m_battery[((bank * 0x2000) + offset) & (m_battery.size() - 1)] = data; + if (!m_prgram.empty()) + m_prgram[((bank * 0x2000) + offset) & (m_prgram.size() - 1)] = data; + } +} + +READ8_MEMBER(nes_sxrom_device::read_m) +{ + UINT8 bank = (m_reg[1] >> 2) & 3; + LOG_MMC(("sxrom read_m, offset: %04x\n", offset)); + + if (!BIT(m_reg[3], 4)) // WRAM enabled + { + if (!m_battery.empty()) + return m_battery[((bank * 0x2000) + offset) & (m_battery.size() - 1)]; + if (!m_prgram.empty()) + return m_prgram[((bank * 0x2000) + offset) & (m_prgram.size() - 1)]; + } + + return m_open_bus; // open bus +} + +// SOROM has two RAM banks, the first is not battery backed up, the second is. +WRITE8_MEMBER(nes_sorom_device::write_m) +{ + UINT8 type = BIT(m_reg[0], 4) ? BIT(m_reg[1], 4) : BIT(m_reg[1], 3); + LOG_MMC(("sorom write_m, offset: %04x, data: %02x\n", offset, data)); + + if (!BIT(m_reg[3], 4)) // WRAM enabled + { + if (type) + m_battery[offset & (m_battery.size() - 1)] = data; + else + m_prgram[offset & (m_prgram.size() - 1)] = data; + } +} + +READ8_MEMBER(nes_sorom_device::read_m) +{ + UINT8 type = BIT(m_reg[0], 4) ? BIT(m_reg[1], 4) : BIT(m_reg[1], 3); + LOG_MMC(("sorom read_m, offset: %04x\n", offset)); + + if (!BIT(m_reg[3], 4)) // WRAM enabled + { + if (type) + return m_battery[offset & (m_battery.size() - 1)]; + else + return m_prgram[offset & (m_prgram.size() - 1)]; + } + + return m_open_bus; // open bus +} + +// MMC1A boards have no wram enable/disable bit +WRITE8_MEMBER(nes_sxrom_a_device::write_m) +{ + UINT8 bank = (m_reg[1] >> 2) & 3; + LOG_MMC(("sxrom_a write_m, offset: %04x, data: %02x\n", offset, data)); + + if (!m_battery.empty()) + m_battery[((bank * 0x2000) + offset) & (m_battery.size() - 1)] = data; + if (!m_prgram.empty()) + m_prgram[((bank * 0x2000) + offset) & (m_prgram.size() - 1)] = data; +} + +READ8_MEMBER(nes_sxrom_a_device::read_m) +{ + UINT8 bank = (m_reg[1] >> 2) & 3; + LOG_MMC(("sxrom_a read_m, offset: %04x\n", offset)); + + if (!m_battery.empty()) + return m_battery[((bank * 0x2000) + offset) & (m_battery.size() - 1)]; + if (!m_prgram.empty()) + return m_prgram[((bank * 0x2000) + offset) & (m_prgram.size() - 1)]; + + return m_open_bus; // open bus +} + +WRITE8_MEMBER(nes_sorom_a_device::write_m) +{ + UINT8 type = BIT(m_reg[0], 4) ? BIT(m_reg[1], 4) : BIT(m_reg[1], 3); + LOG_MMC(("sorom_a write_m, offset: %04x, data: %02x\n", offset, data)); + + if (type) + m_battery[offset & (m_battery.size() - 1)] = data; + else + m_prgram[offset & (m_prgram.size() - 1)] = data; +} + +READ8_MEMBER(nes_sorom_a_device::read_m) +{ + UINT8 type = BIT(m_reg[0], 4) ? BIT(m_reg[1], 4) : BIT(m_reg[1], 3); + LOG_MMC(("sorom_a read_m, offset: %04x\n", offset)); + + if (type) + return m_battery[offset & (m_battery.size() - 1)]; + else + return m_prgram[offset & (m_prgram.size() - 1)]; +} diff --git a/src/devices/bus/nes/mmc1.h b/src/devices/bus/nes/mmc1.h new file mode 100644 index 00000000000..05e25c46818 --- /dev/null +++ b/src/devices/bus/nes/mmc1.h @@ -0,0 +1,82 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_MMC1_H +#define __NES_MMC1_H + +#include "nxrom.h" + + +// ======================> nes_sxrom_device + +class nes_sxrom_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sxrom_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_sxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void update_regs(int reg); // this is needed to simplify NES-EVENT pcb implementation, which handle differently some regs! + + virtual void pcb_reset(); + +protected: + TIMER_CALLBACK_MEMBER(resync_callback); + virtual void set_prg(); + virtual void set_chr(); + + UINT8 m_reg[4]; + int m_reg_write_enable; + int m_latch; + int m_count; +}; + +class nes_sorom_device : public nes_sxrom_device +{ +public: + // construction/destruction + nes_sorom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + +class nes_sxrom_a_device : public nes_sxrom_device +{ +public: + // construction/destruction + nes_sxrom_a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); +}; + +class nes_sorom_a_device : public nes_sxrom_device +{ +public: + // construction/destruction + nes_sorom_a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + + + + +// device type definition +extern const device_type NES_SXROM; +extern const device_type NES_SOROM; +extern const device_type NES_SXROM_A; +extern const device_type NES_SOROM_A; + + +#endif diff --git a/src/devices/bus/nes/mmc2.c b/src/devices/bus/nes/mmc2.c new file mode 100644 index 00000000000..b9ef5402a00 --- /dev/null +++ b/src/devices/bus/nes/mmc2.c @@ -0,0 +1,198 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Nintendo MMC-2 & MMC-4 PCBs + + + Here we emulate the Nintendo PxROM and FxROM PCBs [mapper 9 & 10] + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "mmc2.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_PXROM = &device_creator; +const device_type NES_FXROM = &device_creator; + + +nes_pxrom_device::nes_pxrom_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_pxrom_device::nes_pxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_PXROM, "NES Cart PxROM (MMC-2) PCB", tag, owner, clock, "nes_pxrom", __FILE__) +{ +} + +nes_fxrom_device::nes_fxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_pxrom_device(mconfig, NES_FXROM, "NES Cart FxROM (MMC-4) PCB", tag, owner, clock, "nes_fxrom", __FILE__) +{ +} + + + + +void nes_pxrom_device::device_start() +{ + common_start(); + save_item(NAME(m_latch1)); + save_item(NAME(m_latch2)); + save_item(NAME(m_reg)); +} + +void nes_pxrom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg8_89(0); + prg8_ab((m_prg_chunks << 1) - 3); + prg8_cd((m_prg_chunks << 1) - 2); + prg8_ef((m_prg_chunks << 1) - 1); + chr8(0, m_chr_source); + + m_reg[0] = m_reg[2] = 0; + m_reg[1] = m_reg[3] = 0; + m_latch1 = m_latch2 = 0xfe; +} + +void nes_fxrom_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_reg[0] = m_reg[2] = 0; + m_reg[1] = m_reg[3] = 0; + m_latch1 = m_latch2 = 0xfe; +} + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + PxROM (MMC2 based) board emulation + + Games: Punch Out!!, Mike Tyson's Punch Out!! + + iNES: mapper 9 + + In MESS: Supported + + -------------------------------------------------*/ + +void nes_pxrom_device::ppu_latch(offs_t offset) +{ + if ((offset & 0x3ff0) == 0x0fd0) + { + LOG_MMC(("mmc2 vrom latch switch (bank 0 low): %02x\n", m_reg[0])); + m_latch1 = 0xfd; + chr4_0(m_reg[0], CHRROM); + } + else if ((offset & 0x3ff0) == 0x0fe0) + { + LOG_MMC(("mmc2 vrom latch switch (bank 0 high): %02x\n", m_reg[1])); + m_latch1 = 0xfe; + chr4_0(m_reg[1], CHRROM); + } + else if ((offset & 0x3ff0) == 0x1fd0) + { + LOG_MMC(("mmc2 vrom latch switch (bank 1 low): %02x\n", m_reg[2])); + m_latch2 = 0xfd; + chr4_4(m_reg[2], CHRROM); + } + else if ((offset & 0x3ff0) == 0x1fe0) + { + LOG_MMC(("mmc2 vrom latch switch (bank 0 high): %02x\n", m_reg[3])); + m_latch2 = 0xfe; + chr4_4(m_reg[3], CHRROM); + } +} + +WRITE8_MEMBER( nes_pxrom_device::pxrom_write ) +{ + LOG_MMC(("pxrom write_h, offset: %04x, data: %02x\n", offset, data)); + switch (offset & 0x7000) + { + case 0x2000: + prg8_89(data); + break; + case 0x3000: + m_reg[0] = data; + if (m_latch1 == 0xfd) + chr4_0(m_reg[0], CHRROM); + break; + case 0x4000: + m_reg[1] = data; + if (m_latch1 == 0xfe) + chr4_0(m_reg[1], CHRROM); + break; + case 0x5000: + m_reg[2] = data; + if (m_latch2 == 0xfd) + chr4_4(m_reg[2], CHRROM); + break; + case 0x6000: + m_reg[3] = data; + if (m_latch2 == 0xfe) + chr4_4(m_reg[3], CHRROM); + break; + case 0x7000: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + default: + LOG_MMC(("MMC2 uncaught w: %04x:%02x\n", offset, data)); + break; + } +} + +/*------------------------------------------------- + + FxROM (MMC4 based) board emulation + + Games: Famicom Wars, Fire Emblem, Fire Emblem Gaiden + + This is a small hardware variants of MMC2 (additional + prg bankswitch line) + + iNES: mapper 10 + + In MESS: Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER( nes_fxrom_device::write_h ) +{ + LOG_MMC(("fxrom write_h, offset: %04x, data: %02x\n", offset, data)); + switch (offset & 0x7000) + { + case 0x2000: + prg16_89ab(data); + break; + default: + pxrom_write(space, offset, data, mem_mask); + break; + } +} diff --git a/src/devices/bus/nes/mmc2.h b/src/devices/bus/nes/mmc2.h new file mode 100644 index 00000000000..205fcf7a1d4 --- /dev/null +++ b/src/devices/bus/nes/mmc2.h @@ -0,0 +1,54 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_MMC2_H +#define __NES_MMC2_H + +#include "nxrom.h" + + +// ======================> nes_pxrom_device + +class nes_pxrom_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_pxrom_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_pxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(pxrom_write); + virtual DECLARE_WRITE8_MEMBER(write_h) { pxrom_write(space, offset, data, mem_mask); } + + virtual void ppu_latch(offs_t offset); + virtual void pcb_reset(); + +protected: + UINT8 m_reg[4]; + int m_latch1, m_latch2; +}; + + +// ======================> nes_fxrom_device + +class nes_fxrom_device : public nes_pxrom_device +{ +public: + // construction/destruction + nes_fxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + + + + +// device type definition +extern const device_type NES_PXROM; +extern const device_type NES_FXROM; + +#endif diff --git a/src/devices/bus/nes/mmc3.c b/src/devices/bus/nes/mmc3.c new file mode 100644 index 00000000000..8e81fac286f --- /dev/null +++ b/src/devices/bus/nes/mmc3.c @@ -0,0 +1,560 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Nintendo MMC-3 & MMC-6 PCBs + + + Here we emulate the following PCBs + + * Nintendo TxROM, aka MMC-3 [mapper 4] + * Nintendo HKROM, aka MMC-6 [mapper 4] + * Nintendo TxSROM [mapper 118] + * Nintendo TQROM [mapper 119] + * Nintendo NES-QJ [mapper 47] + * Nintendo PAL-ZZ [mapper 37] + + + Known issues on specific mappers: + + * 004 Mendel Palace has never worked properly + * 004 Ninja Gaiden 2 has flashing bg graphics in the second level + * 119 Pin Bot has glitches when the ball is in the upper half of the screen + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "mmc3.h" + +#include "cpu/m6502/m6502.h" +#include "video/ppu2c0x.h" // this has to be included so that IRQ functions can access PPU_BOTTOM_VISIBLE_SCANLINE + + +#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_TXROM = &device_creator; +const device_type NES_HKROM = &device_creator; +const device_type NES_TXSROM = &device_creator; +const device_type NES_TQROM = &device_creator; +const device_type NES_QJ_PCB = &device_creator; +const device_type NES_ZZ_PCB = &device_creator; + + +nes_txrom_device::nes_txrom_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_txrom_device::nes_txrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_TXROM, "NES Cart TxROM (MMC-3) PCB", tag, owner, clock, "nes_txrom", __FILE__) +{ +} + +nes_hkrom_device::nes_hkrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_HKROM, "NES Cart HKROM (MMC-6) PCB", tag, owner, clock, "nes_hkrom", __FILE__) +{ +} + +nes_txsrom_device::nes_txsrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_TXSROM, "NES Cart TxSROM PCB", tag, owner, clock, "nes_txsrom", __FILE__) +{ +} + +nes_tqrom_device::nes_tqrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_TQROM, "NES Cart TQROM PCB", tag, owner, clock, "nes_tqrom", __FILE__) +{ +} + +nes_qj_device::nes_qj_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_QJ_PCB, "NES Cart NES-QJ PCB", tag, owner, clock, "nes_qj", __FILE__) +{ +} + +nes_zz_device::nes_zz_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_ZZ_PCB, "NES Cart PAL-ZZ PCB", tag, owner, clock, "nes_zz", __FILE__) +{ +} + + + +void nes_txrom_device::mmc3_start() +{ + common_start(); + save_item(NAME(m_mmc_prg_bank)); + save_item(NAME(m_mmc_vrom_bank)); + save_item(NAME(m_latch)); + save_item(NAME(m_wram_protect)); + save_item(NAME(m_prg_base)); + save_item(NAME(m_prg_mask)); + save_item(NAME(m_chr_base)); + save_item(NAME(m_chr_mask)); + save_item(NAME(m_alt_irq)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); + save_item(NAME(m_irq_clear)); +} + +void nes_txrom_device::mmc3_common_initialize( int prg_mask, int chr_mask, int irq_type ) +{ + m_mmc_prg_bank[0] = m_mmc_prg_bank[2] = 0xffe; // m_mmc_prg_bank[2] & m_mmc_prg_bank[3] remain always the same in most MMC3 variants + m_mmc_prg_bank[1] = m_mmc_prg_bank[3] = 0xfff; // but some pirate clone mappers change them after writing certain registers + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); + + m_latch = 0; + m_wram_protect = 0x80; + + m_prg_base = m_chr_base = 0; + m_prg_mask = prg_mask; + m_chr_mask = chr_mask; + + m_alt_irq = irq_type; // later MMC3 boards seem to use MMC6-type IRQ... more investigations are in progress at NESDev... + m_irq_enable = 0; + m_irq_count = m_irq_count_latch = 0; + m_irq_clear = 0; + + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); +} + + +void nes_txrom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + // 4-screen mirroring is taken care in pcb_start, if needed... + mmc3_common_initialize(0xff, 0xff, 0); +} + + +void nes_hkrom_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_mmc6_reg)); + save_item(NAME(m_wram_enable)); + + save_item(NAME(m_mmc6_ram)); + + m_mapper_sram_size = 0x400; + m_mapper_sram = m_mmc6_ram; +} + +void nes_hkrom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + mmc3_common_initialize(0xff, 0xff, 1); + m_mmc6_reg = 0xf0; + m_wram_enable = 0; +} + + +void nes_qj_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + mmc3_common_initialize(0x0f, 0x7f, 0); +} + + +void nes_zz_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + mmc3_common_initialize(0x07, 0x7f, 0); +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + TxROM (MMC3 based) board emulation + + iNES: mapper 4 + + -------------------------------------------------*/ + +/* Here, IRQ counter decrements every scanline. */ +void nes_txrom_device::hblank_irq(int scanline, int vblank, int blanked) +{ + if (scanline < PPU_BOTTOM_VISIBLE_SCANLINE) + { + int prior_count = m_irq_count; + if ((m_irq_count == 0) || m_irq_clear) + m_irq_count = m_irq_count_latch; + else + m_irq_count--; + + if (m_irq_enable && !blanked && (m_irq_count == 0) && (prior_count || m_irq_clear /*|| !m_mmc3_alt_irq*/)) // according to blargg the latter should be present as well, but it breaks Rampart and Joe & Mac US: they probably use the alt irq! + { + LOG_MMC(("irq fired, scanline: %d (MAME %d, beam pos: %d)\n", scanline, + machine().first_screen()->vpos(), machine().first_screen()->hpos())); + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + } + } + m_irq_clear = 0; +} + +// base MMC3 simply calls prg8_x +void nes_txrom_device::prg_cb( int start, int bank ) +{ + prg8_x(start, bank); +} + +// base MMC3 simply calls chr1_x +void nes_txrom_device::chr_cb( int start, int bank, int source ) +{ + chr1_x(start, bank, source); +} + + +void nes_txrom_device::set_prg( int prg_base, int prg_mask ) +{ + UINT8 prg_flip = (m_latch & 0x40) ? 2 : 0; + + prg_cb(0, prg_base | (m_mmc_prg_bank[0 ^ prg_flip] & prg_mask)); + prg_cb(1, prg_base | (m_mmc_prg_bank[1] & prg_mask)); + prg_cb(2, prg_base | (m_mmc_prg_bank[2 ^ prg_flip] & prg_mask)); + prg_cb(3, prg_base | (m_mmc_prg_bank[3] & prg_mask)); +} + +void nes_txrom_device::set_chr( UINT8 chr, int chr_base, int chr_mask ) +{ + UINT8 chr_page = (m_latch & 0x80) >> 5; + + chr_cb(chr_page ^ 0, chr_base | ((m_mmc_vrom_bank[0] & ~0x01) & chr_mask), chr); + chr_cb(chr_page ^ 1, chr_base | ((m_mmc_vrom_bank[0] | 0x01) & chr_mask), chr); + chr_cb(chr_page ^ 2, chr_base | ((m_mmc_vrom_bank[1] & ~0x01) & chr_mask), chr); + chr_cb(chr_page ^ 3, chr_base | ((m_mmc_vrom_bank[1] | 0x01) & chr_mask), chr); + chr_cb(chr_page ^ 4, chr_base | (m_mmc_vrom_bank[2] & chr_mask), chr); + chr_cb(chr_page ^ 5, chr_base | (m_mmc_vrom_bank[3] & chr_mask), chr); + chr_cb(chr_page ^ 6, chr_base | (m_mmc_vrom_bank[4] & chr_mask), chr); + chr_cb(chr_page ^ 7, chr_base | (m_mmc_vrom_bank[5] & chr_mask), chr); +} + +WRITE8_MEMBER(nes_txrom_device::txrom_write) +{ + UINT8 mmc_helper, cmd; + + LOG_MMC(("txrom_write, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x0000: + mmc_helper = m_latch ^ data; + m_latch = data; + + // Has PRG Mode changed? + if (mmc_helper & 0x40) + set_prg(m_prg_base, m_prg_mask); + + // Has CHR Mode changed? + if (mmc_helper & 0x80) + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + + case 0x0001: + cmd = m_latch & 0x07; + switch (cmd) + { + case 0: case 1: // these do not need to be separated: we take care of them in set_chr! + case 2: case 3: case 4: case 5: + m_mmc_vrom_bank[cmd] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + case 6: + case 7: + m_mmc_prg_bank[cmd - 6] = data; + set_prg(m_prg_base, m_prg_mask); + break; + } + break; + + case 0x2000: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + + case 0x2001: + m_wram_protect = data; + break; + + case 0x4000: + m_irq_count_latch = data; + break; + + case 0x4001: + m_irq_count = 0; + break; + + case 0x6000: + m_irq_enable = 0; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + + case 0x6001: + m_irq_enable = 1; + break; + + default: + logerror("txrom_write uncaught: %04x value: %02x\n", offset + 0x8000, data); + break; + } +} + +WRITE8_MEMBER(nes_txrom_device::write_m) +{ + LOG_MMC(("txrom write_m, offset: %04x, data: %02x\n", offset, data)); + + if (BIT(m_wram_protect, 7) && !BIT(m_wram_protect, 6)) + { + if (!m_battery.empty()) + m_battery[offset & (m_battery.size() - 1)] = data; + if (!m_prgram.empty()) + m_prgram[offset & (m_prgram.size() - 1)] = data; + } +} + +READ8_MEMBER(nes_txrom_device::read_m) +{ + LOG_MMC(("txrom read_m, offset: %04x\n", offset)); + + if (BIT(m_wram_protect, 7)) + { + if (!m_battery.empty()) + return m_battery[offset & (m_battery.size() - 1)]; + if (!m_prgram.empty()) + return m_prgram[offset & (m_prgram.size() - 1)]; + } + + return m_open_bus; // open bus +} + + +/*------------------------------------------------- + + HKROM (MMC6 based) board emulation + + iNES: mapper 4 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_hkrom_device::write_m) +{ + UINT8 write_hi, write_lo; + LOG_MMC(("hkrom write_m, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 0x1000) + return; + + // banks can be written only if both read & write is enabled! + write_hi = ((m_mmc6_reg & 0xc0) == 0xc0); + write_lo = ((m_mmc6_reg & 0x30) == 0x30); + + if (BIT(offset, 9) && write_hi) // access to upper half of 1k + m_mmc6_ram[offset & 0x3ff] = data; + + if (!BIT(offset, 9) && write_lo) // access to lower half of 1k + m_mmc6_ram[offset & 0x3ff] = data; +} + +READ8_MEMBER(nes_hkrom_device::read_m) +{ + LOG_MMC(("hkrom read_m, offset: %04x\n", offset)); + + if (offset < 0x1000) + return m_open_bus; // open bus + + if (!(m_mmc6_reg & 0xa0)) + return m_open_bus; // open bus + + if (BIT(offset, 9) && BIT(m_mmc6_reg, 7)) // access to upper half of 1k when upper read is enabled + return m_mmc6_ram[offset & 0x3ff]; + + if (!BIT(offset, 9) && BIT(m_mmc6_reg, 5)) // access to lower half of 1k when lower read is enabled + return m_mmc6_ram[offset & 0x3ff]; + + // If only one bank is enabled for reading, the other reads back as zero + return 0x00; +} + +WRITE8_MEMBER(nes_hkrom_device::write_h) +{ + UINT8 mmc6_helper; + LOG_MMC(("hkrom write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x0000: + mmc6_helper = m_latch ^ data; + m_latch = data; + + if (!m_wram_enable && BIT(data, 5)) // if WRAM is disabled and has to be enabled, write + m_wram_enable = BIT(data, 5); // (once WRAM has been enabled, it cannot be disabled without resetting the game) + + // Has PRG Mode changed? + if (BIT(mmc6_helper, 6)) + set_prg(m_prg_base, m_prg_mask); + + // Has CHR Mode changed? + if (BIT(mmc6_helper, 7)) + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + + case 0x2001: + if (m_wram_enable) + m_mmc6_reg = data; + break; + + case 0x4001: + m_irq_count = 0; + m_irq_clear = 1; + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + TxSROM (MMC3 based) board emulation + + Games: Armadillo, Play Action Football, Pro Hockey, RPG + Jinsei Game, Y's 3 + + iNES: mapper 118 + + In MESS: Supported. It also uses mmc3_irq. + + -------------------------------------------------*/ + +void nes_txsrom_device::set_mirror() +{ + if (m_latch & 0x80) + { + set_nt_page(0, CIRAM, BIT(m_mmc_vrom_bank[2],7), 1); + set_nt_page(1, CIRAM, BIT(m_mmc_vrom_bank[3],7), 1); + set_nt_page(2, CIRAM, BIT(m_mmc_vrom_bank[4],7), 1); + set_nt_page(3, CIRAM, BIT(m_mmc_vrom_bank[5],7), 1); + } + else + { + set_nt_page(0, CIRAM, BIT(m_mmc_vrom_bank[0],7), 1); + set_nt_page(1, CIRAM, BIT(m_mmc_vrom_bank[0],7), 1); + set_nt_page(2, CIRAM, BIT(m_mmc_vrom_bank[1],7), 1); + set_nt_page(3, CIRAM, BIT(m_mmc_vrom_bank[1],7), 1); + } +} + +void nes_txsrom_device::chr_cb( int start, int bank, int source ) +{ + set_mirror(); // we could probably update only for one (e.g. the first) call, to slightly optimize the code + chr1_x(start, bank, source); +} + +WRITE8_MEMBER(nes_txsrom_device::write_h) +{ + LOG_MMC(("txsrom write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x2000: + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + TQROM (MMC3 based) board emulation + + Games: Pin Bot, High Speed + + iNES: mapper 119 + + In MESS: Supported. It also uses mmc3_irq. + + -------------------------------------------------*/ + +void nes_tqrom_device::set_chr( UINT8 chr, int chr_base, int chr_mask ) +{ + UINT8 chr_page = (m_latch & 0x80) >> 5; + UINT8 src[6], mask[6]; + + // TQROM ignores the source, base and mask set by the MMC3 and determines them based on vrom bank bits + for (int i = 0; i < 6; i++) + { + src[i] = (m_mmc_vrom_bank[i] & 0x40) ? CHRRAM : CHRROM; + mask[i] = (m_mmc_vrom_bank[i] & 0x40) ? 0x07 : 0x3f; + } + + chr1_x(chr_page ^ 0, ((m_mmc_vrom_bank[0] & ~0x01) & mask[0]), src[0]); + chr1_x(chr_page ^ 1, ((m_mmc_vrom_bank[0] | 0x01) & mask[0]), src[0]); + chr1_x(chr_page ^ 2, ((m_mmc_vrom_bank[1] & ~0x01) & mask[1]), src[1]); + chr1_x(chr_page ^ 3, ((m_mmc_vrom_bank[1] | 0x01) & mask[1]), src[1]); + chr1_x(chr_page ^ 4, (m_mmc_vrom_bank[2] & mask[2]), src[2]); + chr1_x(chr_page ^ 5, (m_mmc_vrom_bank[3] & mask[3]), src[3]); + chr1_x(chr_page ^ 6, (m_mmc_vrom_bank[4] & mask[4]), src[4]); + chr1_x(chr_page ^ 7, (m_mmc_vrom_bank[5] & mask[5]), src[5]); +} + + +/*------------------------------------------------- + + NES-QJ board (MMC3 variant for US 2-in-1 Nintendo cart + Super Spike V'Ball + Nintendo World Cup) + + iNES: mapper 47 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_qj_device::write_m) +{ + LOG_MMC(("qj write_m, offset: %04x, data: %02x\n", offset, data)); + + m_prg_base = BIT(data, 0) << 4; + m_prg_mask = 0x0f; + m_chr_base = BIT(data, 0) << 7; + m_chr_mask = 0x7f; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); +} + + +/*------------------------------------------------- + + PAL-ZZ board (MMC3 variant for European 3-in-1 Nintendo cart + Super Mario Bros. + Tetris + Nintendo World Cup) + + iNES: mapper 37 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_zz_device::write_m) +{ + UINT8 mmc_helper = data & 0x07; + LOG_MMC(("zz write_m, offset: %04x, data: %02x\n", offset, data)); + + m_prg_base = (BIT(mmc_helper, 2) << 4) | (((mmc_helper & 0x03) == 0x03) ? 0x08 : 0); + m_prg_mask = (mmc_helper << 1) | 0x07; + m_chr_base = BIT(mmc_helper, 2) << 7; + m_chr_mask = 0x7f; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); +} diff --git a/src/devices/bus/nes/mmc3.h b/src/devices/bus/nes/mmc3.h new file mode 100644 index 00000000000..ace03588bb4 --- /dev/null +++ b/src/devices/bus/nes/mmc3.h @@ -0,0 +1,146 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_MMC3_H +#define __NES_MMC3_H + +#include "nxrom.h" + + +// ======================> nes_txrom_device + +class nes_txrom_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_txrom_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_txrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { mmc3_start(); } + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(txrom_write); + virtual DECLARE_WRITE8_MEMBER(write_h) { txrom_write(space, offset, data, mem_mask); } + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +protected: + virtual void set_prg(int prg_base, int prg_mask); + virtual void set_chr(UINT8 chr, int chr_base, int chr_mask); + void mmc3_start(); + void mmc3_common_initialize(int prg_mask, int chr_mask, int IRQ_type); + + // are there MMC3 clones which need more regs? + UINT16 m_mmc_prg_bank[4]; + UINT16 m_mmc_vrom_bank[8]; // a few clones need more than the 6 banks used by base MMC3 (e.g. waixing_g) + + int m_prg_base, m_prg_mask; // MMC3 based multigame carts select a block of banks by using these (and then act like normal MMC3), + int m_chr_base, m_chr_mask; // while MMC3 and clones (mapper 118 & 119) simply set them as 0 and 0xff resp. + + int m_latch; + int m_wram_protect; + int m_alt_irq; + + UINT16 m_irq_count, m_irq_count_latch; + UINT8 m_irq_clear; + int m_irq_enable; +}; + + +// ======================> nes_hkrom_device + +class nes_hkrom_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_hkrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + int m_wram_enable; + UINT8 m_mmc6_reg; + + // MMC-6 contains 1K of internal ram, battery backed up + UINT8 m_mmc6_ram[0x400]; +}; + + +// ======================> nes_txsrom_device + +class nes_txsrom_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_txsrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void chr_cb(int start, int bank, int source); + +protected: + void set_mirror(); +}; + + +// ======================> nes_tqrom_device + +class nes_tqrom_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_tqrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + +protected: + virtual void set_chr( UINT8 chr, int chr_base, int chr_mask ); +}; + + +// ======================> nes_qj_device + +class nes_qj_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_qj_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual void pcb_reset(); +}; + + +// ======================> nes_zz_device + +class nes_zz_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_zz_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual void pcb_reset(); +}; + + + + +// device type definition +extern const device_type NES_TXROM; +extern const device_type NES_HKROM; +extern const device_type NES_TXSROM; +extern const device_type NES_TQROM; +extern const device_type NES_QJ_PCB; +extern const device_type NES_ZZ_PCB; + +#endif diff --git a/src/devices/bus/nes/mmc3_clones.c b/src/devices/bus/nes/mmc3_clones.c new file mode 100644 index 00000000000..4a806d0fde2 --- /dev/null +++ b/src/devices/bus/nes/mmc3_clones.c @@ -0,0 +1,2588 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for MMC-3 clone PCBs + + + Here we emulate several pirate PCBs based on MMC-3 boards + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "mmc3_clones.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_NITRA = &device_creator; +const device_type NES_KS7057 = &device_creator; +const device_type NES_SBROS11 = &device_creator; +const device_type NES_MALISB = &device_creator; +const device_type NES_FAMILY4646 = &device_creator; +const device_type NES_PIKAY2K = &device_creator; +const device_type NES_8237 = &device_creator; +const device_type NES_SG_LIONK = &device_creator; +const device_type NES_SG_BOOG = &device_creator; +const device_type NES_KASING = &device_creator; +const device_type NES_KAY = &device_creator; +const device_type NES_H2288 = &device_creator; +const device_type NES_6035052 = &device_creator; +const device_type NES_TXC_TW = &device_creator; +const device_type NES_KOF97 = &device_creator; +const device_type NES_KOF96 = &device_creator; +const device_type NES_SF3 = &device_creator; +const device_type NES_GOUDER = &device_creator; +const device_type NES_SA9602B = &device_creator; +const device_type NES_SACHEN_SHERO = &device_creator; +//const device_type NES_A9746 = &device_creator; + +const device_type NES_FK23C = &device_creator; +const device_type NES_FK23CA = &device_creator; +const device_type NES_S24IN1SC03 = &device_creator; +const device_type NES_BMC_15IN1 = &device_creator; +const device_type NES_BMC_SBIG7 = &device_creator; +const device_type NES_BMC_HIK8 = &device_creator; +const device_type NES_BMC_HIK4 = &device_creator; +const device_type NES_BMC_MARIO7IN1 = &device_creator; +const device_type NES_BMC_GOLD7IN1 = &device_creator; +const device_type NES_BMC_GC6IN1 = &device_creator; +const device_type NES_BMC_411120C = &device_creator; +const device_type NES_BMC_830118C = &device_creator; +const device_type NES_PJOY84 = &device_creator; + + +nes_nitra_device::nes_nitra_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_NITRA, "NES Cart Nitra PCB", tag, owner, clock, "nes_nitra", __FILE__) +{ +} + +nes_ks7057_device::nes_ks7057_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_KS7057, "NES Cart KS-7057 PCB", tag, owner, clock, "nes_ks7057", __FILE__) +{ +} + +nes_sbros11_device::nes_sbros11_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_SBROS11, "NES Cart SMB 11 PCB", tag, owner, clock, "nes_smb11", __FILE__) +{ +} + +nes_malisb_device::nes_malisb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_MALISB, "NES Cart Mali Spash Bomb PCB", tag, owner, clock, "nes_malisb", __FILE__) +{ +} + +nes_family4646_device::nes_family4646_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_FAMILY4646, "NES Cart BMC-FAMILY-4646 PCB", tag, owner, clock, "nes_family4646", __FILE__) +{ +} + +nes_pikay2k_device::nes_pikay2k_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_PIKAY2K, "NES Cart PIKACHU Y2K PCB", tag, owner, clock, "nes_pikay2k", __FILE__) +{ +} + +nes_8237_device::nes_8237_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_8237, "NES Cart UNL-8237 PCB", tag, owner, clock, "nes_8237", __FILE__) +{ +} + +nes_sglionk_device::nes_sglionk_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_SG_LIONK, "NES Cart SuperGame Lion King PCB", tag, owner, clock, "nes_sglionk", __FILE__) +{ +} + +nes_sgboog_device::nes_sgboog_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_SG_BOOG, "NES Cart SuperGame BoogerMan PCB", tag, owner, clock, "nes_sgbooger", __FILE__) +{ +} + +nes_kasing_device::nes_kasing_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_KASING, "NES Cart Kasing PCB", tag, owner, clock, "nes_kasing", __FILE__) +{ +} + +nes_kay_device::nes_kay_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_KAY, "NES Cart KAY PCB", tag, owner, clock, "nes_kay", __FILE__) +{ +} + +nes_h2288_device::nes_h2288_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_H2288, "NES Cart H-2288 PCB", tag, owner, clock, "nes_h2288", __FILE__) +{ +} + +nes_6035052_device::nes_6035052_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_6035052, "NES Cart UNL-603-5052 PCB", tag, owner, clock, "nes_6035052", __FILE__) +{ +} + +nes_txc_tw_device::nes_txc_tw_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_TXC_TW, "NES Cart TXC Thunder Warrior PCB", tag, owner, clock, "nes_txc_tw", __FILE__) +{ +} + +nes_kof97_device::nes_kof97_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_KOF97, "NES Cart KOF 97 PCB", tag, owner, clock, "nes_kof97", __FILE__) +{ +} + +nes_kof96_device::nes_kof96_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_KOF96, "NES Cart KOF 96 PCB", tag, owner, clock, "nes_kof96", __FILE__) +{ +} + +nes_sf3_device::nes_sf3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_SF3, "NES Cart Super Fighter III PCB", tag, owner, clock, "nes_sf3", __FILE__) +{ +} + +nes_gouder_device::nes_gouder_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_GOUDER, "NES Cart Gouder PCB", tag, owner, clock, "nes_gouder", __FILE__) +{ +} + +nes_sa9602b_device::nes_sa9602b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_SA9602B, "NES Cart SA-9602B PCB", tag, owner, clock, "nes_sa9602b", __FILE__) +{ +} + +nes_sachen_shero_device::nes_sachen_shero_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_SACHEN_SHERO, "NES Cart Street Hero PCB", tag, owner, clock, "nes_shero", __FILE__) +{ +} + +//nes_a9746_device::nes_a9746_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) +// : nes_txrom_device(mconfig, NES_A9746, "NES Cart A-9746 PCB", tag, owner, clock, "nes_bmc_a9746", __FILE__) +//{ +//} + +nes_fk23c_device::nes_fk23c_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_txrom_device(mconfig, type, name, tag, owner, clock, shortname, source) +{ +} + +nes_fk23c_device::nes_fk23c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_FK23C, "NES Cart FK23C PCB", tag, owner, clock, "nes_fk23c", __FILE__) +{ +} + +nes_fk23ca_device::nes_fk23ca_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_fk23c_device(mconfig, NES_FK23CA, "NES Cart FK23CA PCB", tag, owner, clock, "nes_fk23ca", __FILE__) +{ +} + +nes_s24in1sc03_device::nes_s24in1sc03_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_S24IN1SC03, "NES Cart Super 24 in 1 SC-03 PCB", tag, owner, clock, "nes_s24in1c03", __FILE__) +{ +} + +nes_bmc_15in1_device::nes_bmc_15in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_BMC_15IN1, "NES Cart BMC 15 in 1 PCB", tag, owner, clock, "nes_bmc_15in1", __FILE__) +{ +} + +nes_bmc_sbig7_device::nes_bmc_sbig7_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_BMC_SBIG7, "NES Cart BMC Super BIG 7 in 1 PCB", tag, owner, clock, "nes_bmc_sbit7", __FILE__) +{ +} + +nes_bmc_hik8_device::nes_bmc_hik8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_BMC_HIK8, "NES Cart BMC Super HIK 8 in 1 PCB", tag, owner, clock, "nes_bmc_hik8", __FILE__) +{ +} + +nes_bmc_hik4_device::nes_bmc_hik4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_BMC_HIK4, "NES Cart BMC Super HIK 4 in 1 PCB", tag, owner, clock, "nes_bmc_hik4", __FILE__) +{ +} + +nes_bmc_mario7in1_device::nes_bmc_mario7in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_BMC_MARIO7IN1, "NES Cart BMC Mario 7 in 1 PCB", tag, owner, clock, "nes_bmc_mario7in1", __FILE__) +{ +} + +nes_bmc_gold7in1_device::nes_bmc_gold7in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_BMC_GOLD7IN1, "NES Cart BMC Golden 7 in 1 PCB", tag, owner, clock, "nes_bmc_gold7in1", __FILE__) +{ +} + +nes_bmc_gc6in1_device::nes_bmc_gc6in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_BMC_GC6IN1, "NES Cart BMC Golden Card 6 in 1 PCB", tag, owner, clock, "nes_bmc_gc6in1", __FILE__) +{ +} + +nes_bmc_411120c_device::nes_bmc_411120c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_BMC_411120C, "NES Cart BMC 411120C PCB", tag, owner, clock, "nes_bmc_411120c", __FILE__) +{ +} + +nes_bmc_830118c_device::nes_bmc_830118c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_BMC_830118C, "NES Cart BMC 830118C PCB", tag, owner, clock, "nes_bmc_830118c", __FILE__) +{ +} + +nes_pjoy84_device::nes_pjoy84_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_PJOY84, "NES Cart Powerjoy 84 PCB", tag, owner, clock, "nes_pjoy84", __FILE__) +{ +} + + + +void nes_family4646_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + mmc3_common_initialize(0x1f, 0xff, 0); +} + +void nes_pikay2k_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_pikay2k_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg[0] = 0xff; + m_reg[1] = 0; + mmc3_common_initialize(0xff, 0xff, 0); +} + +void nes_8237_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); + save_item(NAME(m_cd_enable)); +} + +void nes_8237_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg[0] = 0; + m_reg[1] = 0; + m_reg[2] = 0; + m_cd_enable = 0; + mmc3_common_initialize(0xff, 0xff, 0); +} + +void nes_sglionk_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); + save_item(NAME(m_reg_enabled)); +} + +void nes_sglionk_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg = m_reg_enabled = 0; + mmc3_common_initialize(0xff, 0xff, 0); +} + +void nes_sgboog_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); + save_item(NAME(m_mode)); +} + +void nes_sgboog_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_mode = 0x04; + m_reg[0] = 0x00; + m_reg[1] = 0xff; + m_reg[2] = 0; + mmc3_common_initialize(0x1f, 0xff, 0); + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); +} + +void nes_kasing_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_kasing_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg = 0; + mmc3_common_initialize(0xff, 0xff, 0); +} + + +void nes_kay_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_low_reg)); + save_item(NAME(m_reg)); +} + +void nes_kay_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + memset(m_reg, 0, sizeof(m_reg)); + m_low_reg = 0; + mmc3_common_initialize(0xff, 0xff, 0); +} + + +void nes_h2288_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_h2288_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg[0] = 0; + m_reg[1] = 0; + mmc3_common_initialize(0xff, 0xff, 0); +} + +void nes_6035052_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_prot)); +} + +void nes_6035052_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + mmc3_common_initialize(0xff, 0xff, 0); + + m_prot = 0; +} + +void nes_kof96_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_kof96_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + memset(m_reg, 0, sizeof(m_reg)); + mmc3_common_initialize(0xff, 0xff, 0); +} + +void nes_gouder_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_gouder_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + memset(m_reg, 0, sizeof(m_reg)); + mmc3_common_initialize(0xff, 0xff, 0); +} + +void nes_sa9602b_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); + save_item(NAME(m_prg_chip)); +} + +void nes_sa9602b_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg = 0; + m_prg_chip = 0; + mmc3_common_initialize(0x1ff, 0xff, 0); // 1.5MB of PRG-ROM, no CHR-ROM but 32K CHR-RAM +} + +void nes_sachen_shero_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_sachen_shero_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg = 0; + mmc3_common_initialize(0xff, 0x1ff, 0); +} + +void nes_fk23c_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); + save_item(NAME(m_mmc_cmd1)); +} + +void nes_fk23c_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_mmc_cmd1 = 0; + m_reg[0] = 4; + m_reg[1] = 0xff; + m_reg[2] = m_reg[3] = 0; + m_reg[4] = m_reg[5] = m_reg[6] = m_reg[7] = 0xff; + mmc3_common_initialize(0xff, 0xff, 0); + fk23c_set_prg(); + fk23c_set_chr(); + +} + +void nes_fk23ca_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_mmc_cmd1 = 0; + m_reg[0] = m_reg[1] = m_reg[2] = m_reg[3] = 0; + m_reg[4] = m_reg[5] = m_reg[6] = m_reg[7] = 0xff; + mmc3_common_initialize(0xff, 0xff, 0); + fk23c_set_prg(); + fk23c_set_chr(); +} + +void nes_s24in1sc03_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_s24in1sc03_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg[0] = 0x24; + m_reg[1] = 0x9f; + m_reg[2] = 0; + mmc3_common_initialize(0xff, 0xff, 0); +} + +void nes_bmc_15in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + mmc3_common_initialize(0x1f, 0xff, 0); + m_prg_base = 0x10; // this board has a diff prg_base + set_prg(m_prg_base, m_prg_mask); +} + +void nes_bmc_sbig7_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + mmc3_common_initialize(0x0f, 0x7f, 0); +} + +void nes_bmc_hik8_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); + save_item(NAME(m_count)); +} + +void nes_bmc_hik8_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_count = 0; + memset(m_reg, 0, sizeof(m_reg)); + mmc3_common_initialize(0x3f, 0xff, 0); +} + +void nes_bmc_hik4_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + mmc3_common_initialize(0x0f, 0x7f, 0); +} + +void nes_bmc_mario7in1_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg_written)); +} + +void nes_bmc_mario7in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg_written = 0; + mmc3_common_initialize(0x1f, 0xff, 0); +} + +void nes_bmc_gold7in1_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg_written)); +} + +void nes_bmc_gold7in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg_written = 0; + mmc3_common_initialize(0x1f, 0xff, 0); +} + +void nes_bmc_gc6in1_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_bmc_gc6in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg[0] = 0x00; + m_reg[1] = 0xff; + m_reg[2] = 0x03; + m_reg[3] = 0; + mmc3_common_initialize(0xff, 0xff, 0); + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); +} + +void nes_bmc_411120c_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_bmc_411120c_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg = 0; + mmc3_common_initialize(0x7f, 0x7f, 0); +} + +void nes_bmc_830118c_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_bmc_830118c_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg = 0; + mmc3_common_initialize(0x7f, 0x7f, 0); +} + +void nes_pjoy84_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_pjoy84_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + memset(m_reg, 0, sizeof(m_reg)); + set_base_mask(); + mmc3_common_initialize(m_prg_mask, m_chr_mask, 0); +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Bootleg Board by Nitra + + Games: Time Diver Avenger + + This acts basically like a MMC3 with different use of write + address. + + iNES: mapper 250 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_nitra_device::write_h) +{ + LOG_MMC(("nitra write_h, offset: %04x, data: %02x\n", offset, data)); + + txrom_write(space, (offset & 0x6000) | ((offset & 0x400) >> 10), offset & 0xff, mem_mask); +} + +/*------------------------------------------------- + + Board UNL-KS7057 + + Games: Street Fighter VI / Fight Street VI + + MMC3 clone (identical, but for switched address lines) + + In MESS: Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_ks7057_device::write_h) +{ + LOG_MMC(("ks7057 write_h, offset: %04x, data: %02x\n", offset, data)); + + offset = (BIT(offset, 0) << 1) | BIT(offset, 1) | (offset & ~0x03); + txrom_write(space, offset, data, mem_mask); +} + +/*------------------------------------------------- + + BTL-SUPERBROS11 + + Games: Super Mario Bros. 11, Super Mario Bros. 17 + + This acts basically like a MMC3 with different use of write + address. + + iNES: mapper 196 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sbros11_device::write_h) +{ + LOG_MMC(("smb11 write_h, offset: %04x, data: %02x\n", offset, data)); + + txrom_write(space, (offset & 0x6000) | ((offset & 0x04) >> 2), data, mem_mask); +} + +/*------------------------------------------------- + + UNL-MALISB + + Games: Super Mali Bros Splash Bomb + + This is very similar to mapper 196, but with additional + data bit swap. + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_malisb_device::prg_cb(int start, int bank) +{ + bank = (bank & 3) | ((bank & 8) >> 1) | ((bank & 4) << 1); + prg8_x(start, bank); +} + +void nes_malisb_device::chr_cb(int start, int bank, int source) +{ + bank = (bank & 0xdd) | ((bank & 0x20) >> 4) | ((bank & 2) << 4); + chr1_x(start, bank, source); +} + +WRITE8_MEMBER(nes_malisb_device::write_h) +{ + LOG_MMC(("malisb write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset > 0x4000) + txrom_write(space, (offset & 0xfffe) | ((offset & 0x04) >> 2) | ((offset & 0x08) >> 3), data, mem_mask); + else + txrom_write(space, (offset & 0xfffe) | ((offset & 0x08) >> 3), data, mem_mask); +} + +/*------------------------------------------------- + + BMC-FAMILY-4646B + + Known Boards: Unknown Multigame Bootleg Board (4646B) + Games: 2 in 1 - Family Kid & Aladdin 4 + + MMC3 clone + + iNES: mapper 134 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_family4646_device::write_m) +{ + LOG_MMC(("family4646 write_m, offset: %04x, data: %02x\n", offset, data)); + + if (offset == 0x01) + { + m_prg_base = (data & 0x02) << 4; + m_prg_mask = 0x1f; + m_chr_base = (data & 0x20) << 3; + m_chr_mask = 0xff; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } +} + +/*------------------------------------------------- + + BTL-PIKACHUY2K + + Games: Pikachu Y2k + + iNES: mapper 254 + + In MESS: + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_pikay2k_device::write_h) +{ + LOG_MMC(("pikay2k write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x2001: + m_reg[1] = data; + break; + + case 0x2000: + m_reg[0] = 0; + default: + txrom_write(space, offset, data, mem_mask); + break; + } +} + +// strange WRAM usage: it is protected at start, and gets unprotected after the first write to 0xa000 +WRITE8_MEMBER(nes_pikay2k_device::write_m) +{ + LOG_MMC(("pikay2k write_m, offset: %04x, data: %02x\n", offset, data)); + + m_prgram[offset & 0x1fff] = data; +} + +READ8_MEMBER(nes_pikay2k_device::read_m) +{ + LOG_MMC(("pikay2k read_m, offset: %04x\n", offset)); + + return m_prgram[offset & 0x1fff] ^ (m_reg[0] & m_reg[1]); +} + +/*------------------------------------------------- + + Board UNL-8237 + + Games: Pocahontas 2 + + MMC3 clone + + In MESS: Supported + + -------------------------------------------------*/ + +void nes_8237_device::prg_cb(int start, int bank) +{ + if (!(m_reg[0] & 0x80)) + prg8_x(start, bank); +} + +void nes_8237_device::chr_cb(int start, int bank, int source) +{ + bank |= ((m_reg[1] << 6) & 0x100); + chr1_x(start, bank, source); +} + +WRITE8_MEMBER(nes_8237_device::write_l) +{ + LOG_MMC(("unl_8237 write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset == 0x1000) + { + m_reg[0] = data; + if (m_reg[0] & 0x80) + { + if (m_reg[0] & 0x20) + prg32((m_reg[0] & 0x0f) >> 1); + else + { + prg16_89ab(m_reg[0] & 0x1f); + prg16_cdef(m_reg[0] & 0x1f); + } + } + else + set_prg(m_prg_base, m_prg_mask); + } + + if (offset == 0x1001) + { + m_reg[1] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } + + if (offset == 0x1007) + { + m_reg[2] = data & 0x07; // this selects different permutations for addresses and regs (to be implemented) + } +} + +WRITE8_MEMBER(nes_8237_device::write_h) +{ + static const UINT8 conv_table[8] = {0, 2, 6, 1, 7, 3, 4, 5}; + LOG_MMC(("unl_8237 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x0000: + case 0x1000: + set_nt_mirroring((data | (data >> 7)) & 0x01 ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + + case 0x2000: + case 0x3000: + m_cd_enable = 1; + data = (data & 0xc0) | conv_table[data & 0x07]; + txrom_write(space, 0x0000, data, mem_mask); + break; + + case 0x4000: + case 0x5000: + if (m_cd_enable) + { + m_cd_enable = 0; + txrom_write(space, 0x0001, data, mem_mask); + } + break; + + case 0x6000: + break; + + case 0x7000: + txrom_write(space, 0x6001, data, mem_mask); + txrom_write(space, 0x4000, data, mem_mask); + txrom_write(space, 0x4001, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + Bootleg Board by Super Game + + Games: The Lion King + + MMC3 clone. + + iNES: mapper 114 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sglionk_device::write_m) +{ + LOG_MMC(("sglionk write_m, offset: %04x, data: %02x\n", offset, data)); + + m_reg = data; + + if (m_reg & 0x80) + { + prg16_89ab(data & 0x1f); + prg16_cdef(data & 0x1f); + } + else + set_prg(m_prg_base, m_prg_mask); + +} + +WRITE8_MEMBER(nes_sglionk_device::write_h) +{ + static const UINT8 conv_table[8] = {0, 3, 1, 5, 6, 7, 2, 4}; + LOG_MMC(("sglionk write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 0x6000) + { + switch (offset & 0x6000) + { + case 0x0000: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 0x2000: + m_reg_enabled = 1; + data = (data & 0xc0) | conv_table[data & 0x07]; + txrom_write(space, 0x0000, data, mem_mask); + break; + case 0x4000: + if (m_reg_enabled && (m_reg & 0x80) == 0) + { + m_reg_enabled = 0; + txrom_write(space, 0x0001, data, mem_mask); + } + break; + } + } + else + { + switch (offset & 0x03) + { + case 0x02: + txrom_write(space, 0x6000, data, mem_mask); + break; + case 0x03: + txrom_write(space, 0x6001, data, mem_mask); + txrom_write(space, 0x4000, data, mem_mask); + txrom_write(space, 0x4001, data, mem_mask); + break; + } + } +} + +/*------------------------------------------------- + + Bootleg Board by Super Game + + Games: Boogerman, Mortal Kombat III + + MMC3 clone. Also, it probably needs a hack to support both + variants (Boogerman & MK3). + + iNES: mapper 215 + + In MESS: Preliminary support. + + -------------------------------------------------*/ + +void nes_sgboog_device::prg_cb(int start, int bank) +{ + if (!(m_reg[0] & 0x80)) // if this is != 0 we should never even arrive here + { + if (m_reg[1] & 0x08) + bank = (bank & 0x1f) | 0x20; + else + bank = (bank & 0x0f) | (m_reg[1] & 0x10); + + prg8_x(start, bank); + } +} + +void nes_sgboog_device::chr_cb(int start, int bank, int source) +{ + if ((m_reg[1] & 0x04)) + bank |= 0x100; + else + bank = (bank & 0x7f) | ((m_reg[1] & 0x10) << 3); + + chr1_x(start, bank, source); +} + +void nes_sgboog_device::set_prg(int prg_base, int prg_mask) +{ + if (m_reg[0] & 0x80) + { + prg16_89ab((m_reg[0] & 0xf0) | (m_reg[1] & 0x10)); + prg16_cdef((m_reg[0] & 0xf0) | (m_reg[1] & 0x10)); + } + else + { + // here standard MMC3 bankswitch + UINT8 prg_flip = (m_latch & 0x40) ? 2 : 0; + + prg_cb(0, prg_base | (m_mmc_prg_bank[0 ^ prg_flip] & prg_mask)); + prg_cb(1, prg_base | (m_mmc_prg_bank[1] & prg_mask)); + prg_cb(2, prg_base | (m_mmc_prg_bank[2 ^ prg_flip] & prg_mask)); + prg_cb(3, prg_base | (m_mmc_prg_bank[3] & prg_mask)); + } +} + +WRITE8_MEMBER(nes_sgboog_device::write_l) +{ + LOG_MMC(("sgboog write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset == 0x1000) + { + m_reg[0] = data; + set_prg(m_prg_base, m_prg_mask); + } + else if (offset == 0x1001) + { + m_reg[1] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } + else if (offset == 0x1007) + { + m_latch = 0; + m_mode = data; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } +} + +WRITE8_MEMBER(nes_sgboog_device::write_m) +{ + LOG_MMC(("sgboog write_m, offset: %04x, data: %02x\n", offset, data)); + + if (offset == 0x0000) + { + m_reg[0] = data; + set_prg(m_prg_base, m_prg_mask); + } + else if (offset == 0x0001) + { + m_reg[1] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } + else if (offset == 0x0007) + { + m_latch = 0; + m_mode = data; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } +} + +WRITE8_MEMBER(nes_sgboog_device::write_h) +{ + static const UINT8 conv_table[8] = {0,2,5,3,6,1,7,4}; + LOG_MMC(("sgboog write_h, offset: %04x, data: %02x\n", offset, data)); + + if (m_mode) + { + switch (offset & 0x6001) + { + case 0x0000: + break; + + case 0x0001: + if (m_reg[2] && ((m_reg[0] & 0x80) == 0 || (m_latch & 0x07) < 6)) // if we use the prg16 banks and cmd=6,7 DON'T enter! + { + m_reg[2] = 0; + txrom_write(space, 0x0001, data, mem_mask); + } + break; + + case 0x2000: + data = (data & 0xc0) | conv_table[data & 0x07]; + m_reg[2] = 1; + txrom_write(space, 0x0000, data, mem_mask); + break; + + case 0x4000: + set_nt_mirroring(((data >> 7) | data) & 0x01 ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + + case 0x4001: + txrom_write(space, 0x6001, data, mem_mask); + break; + + case 0x6001: + txrom_write(space, 0x4000, data, mem_mask); + txrom_write(space, 0x4001, data, mem_mask); + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } + } + else + txrom_write(space, offset, data, mem_mask); +} + +/*------------------------------------------------- + + Bootleg Board by Kasing + + Games: AV Jiu Ji Mahjong, Bao Qing Tian, Thunderbolt 2, + Shisen Mahjong 2 + + MMC3 clone + + iNES: mapper 115 + + In MESS: Supported + + -------------------------------------------------*/ + +void nes_kasing_device::prg_cb(int start, int bank) +{ + if (BIT(m_reg, 7)) + prg32(m_reg >> 1); + else + prg8_x(start, bank); +} + +WRITE8_MEMBER(nes_kasing_device::write_m) +{ + LOG_MMC(("kasing write_m, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x01) + { + case 0x00: + m_reg = data; + set_prg(m_prg_base, m_prg_mask); + break; + case 0x01: + m_chr_base = (data & 0x01) ? 0x100 : 0x000; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + } +} + +/*------------------------------------------------- + + Bootleg Board by Kay (for Panda Prince) + + Games: The Panda Prince, Sonic 3d Blast 6, SFZ2 '97, YuYu '97 + (and its title hack MK6), UMK3, Super Lion King 2 + + MMC3 clone. This is basically KOF96 board + protection + + iNES: mapper 121 + + In MESS: Most game works, with some graphical issues. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_kay_device::write_l) +{ + LOG_MMC(("kay write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset >= 0x1000) + { + switch (data & 0x03) + { + case 0x00: + case 0x01: + m_low_reg = 0x83; + break; + case 0x02: + m_low_reg = 0x42; + break; + case 0x03: + m_low_reg = 0x00; + break; + } + } +} + +READ8_MEMBER(nes_kay_device::read_l) +{ + LOG_MMC(("kay read_l, offset: %04x\n", offset)); + offset += 0x100; + + if (offset >= 0x1000) + return m_low_reg; + else + return 0xff; +} + +void nes_kay_device::update_regs() +{ + switch (m_reg[5] & 0x3f) + { + case 0x20: + case 0x29: + case 0x2b: + case 0x3f: + m_reg[7] = 1; + m_reg[1] = m_reg[6]; + break; + case 0x26: + m_reg[7] = 0; + m_reg[1] = m_reg[6]; + break; + case 0x2c: + m_reg[7] = 1; + if (m_reg[6]) + m_reg[1] = m_reg[6]; + break; + + case 0x28: + m_reg[7] = 0; + m_reg[2] = m_reg[6]; + break; + + case 0x2a: + m_reg[7] = 0; + m_reg[3] = m_reg[6]; + break; + + case 0x2f: + break; + + default: + m_reg[5] = 0; + break; + } +} + +void nes_kay_device::prg_cb(int start, int bank) +{ + if (m_reg[5] & 0x3f) + { + prg8_x(start, bank & 0x3f); + prg8_ef(m_reg[1]); + prg8_cd(m_reg[2]); + prg8_ab(m_reg[3]); + } + else + prg8_x(start, bank & 0x3f); +} + +void nes_kay_device::chr_cb(int start, int bank, int source) +{ + UINT8 chr_page = (m_latch & 0x80) >> 5; + + if ((start & 0x04) == chr_page) + bank |= 0x100; + + chr1_x(start, bank, source); +} + +WRITE8_MEMBER(nes_kay_device::write_h) +{ + LOG_MMC(("kay write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6003) + { + case 0x0000: + txrom_write(space, offset, data, mem_mask); + set_prg(m_prg_base, m_prg_mask); + break; + + case 0x0001: + m_reg[6] = (BIT(data, 0) << 5) | (BIT(data, 1) << 4) | (BIT(data, 2) << 3) + | (BIT(data, 3) << 2) | (BIT(data, 4) << 1) | BIT(data, 5); + if (!m_reg[7]) + update_regs(); + txrom_write(space, offset, data, mem_mask); + set_prg(m_prg_base, m_prg_mask); + break; + + case 0x0003: + m_reg[5] = data; + update_regs(); + txrom_write(space, 0x0000, data, mem_mask); + set_prg(m_prg_base, m_prg_mask); + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + UNL-H2288 + + -------------------------------------------------*/ + +void nes_h2288_device::prg_cb(int start, int bank) +{ + if (!(m_reg[0] & 0x40)) + prg8_x(start, bank); +} + +WRITE8_MEMBER(nes_h2288_device::write_l) +{ + LOG_MMC(("h2288 write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset >= 0x1800) + { + m_reg[offset & 1] = data; + if (m_reg[0] & 0x40) + { + UINT8 helper1 = (m_reg[0] & 0x05) | ((m_reg[0] >> 2) & 0x0a); + UINT8 helper2 = BIT(m_reg[0], 1); + prg16_89ab(helper1 & ~helper2); + prg16_cdef(helper1 | helper2); + } + else + set_prg(m_prg_base, m_prg_mask); + } +} + +READ8_MEMBER(nes_h2288_device::read_l) +{ + LOG_MMC(("h2288 read_l, offset: %04x\n", offset)); + offset += 0x100; + + if (offset >= 0x1000) + { + int helper = offset >> 8; + if (offset & 1) + return helper | 0x01; + else + return helper ^ 0x01; + } + + return 0; +} + +WRITE8_MEMBER(nes_h2288_device::write_h) +{ + static const UINT8 conv_table[8] = {0, 3, 1, 5, 6, 7, 2, 4}; + LOG_MMC(("h2288 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x0000: + txrom_write(space, 0x0000, (data & 0xc0) | conv_table[data & 0x07], mem_mask); + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + UNL-603-5052 + + MMC3 + protection access in 0x4020 - 0x7fff + + in MESS: Partial support + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_6035052_device::write_ex) +{ + LOG_MMC(("6035052 write_ex, offset: %04x, data: %02x\n", offset, data)); + m_prot = data & 0x03; + if (m_prot == 1) + m_prot = 2; +} + +READ8_MEMBER(nes_6035052_device::read_ex) +{ + LOG_MMC(("6035052 read_ex, offset: %04x\n", offset)); + return m_prot; +} + +/*------------------------------------------------- + + Bootleg Board 'Thunder Warrior' by TXC + + Games: Master Fighter II, Master Fighter 3, Thunder Warrior + + MMC3 clone + + iNES: mapper 189 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_txc_tw_device::write_l) +{ + LOG_MMC(("txc_tw write_l, offset: %04x, data: %02x\n", offset, data)); + + prg32((data >> 4) | data); +} + +/* writes to 0x8000-0xffff are like MMC3 but no PRG bankswitch (beacuse it is handled by low writes) */ +void nes_txc_tw_device::prg_cb(int start, int bank) +{ + return; +} + +/*------------------------------------------------- + + Board UNL-KOF97 + + Games: King of Fighters 97 (Rex Soft) + + MMC3 clone + + In MESS: Not working + + -------------------------------------------------*/ + +inline UINT8 kof97_unscramble( UINT8 data ) +{ + return ((data >> 1) & 0x01) | ((data >> 4) & 0x02) | ((data << 2) & 0x04) | ((data >> 0) & 0xd8) | ((data << 3) & 0x20); +} + +WRITE8_MEMBER(nes_kof97_device::write_h) +{ + LOG_MMC(("kof97 write_h, offset: %04x, data: %02x\n", offset, data)); + + /* Addresses 0x9000, 0xa000, 0xd000 & 0xf000 behaves differently than MMC3 */ + if (offset == 0x1000) + txrom_write(space, 0x0001, kof97_unscramble(data), mem_mask); + else if (offset == 0x2000) + txrom_write(space, 0x0000, kof97_unscramble(data), mem_mask); + else if (offset == 0x5000) + txrom_write(space, 0x4001, kof97_unscramble(data), mem_mask); + else if (offset == 0x7000) + txrom_write(space, 0x6001, kof97_unscramble(data), mem_mask); + /* Other addresses behaves like MMC3, up to unscrambling data */ + else + txrom_write(space, offset, kof97_unscramble(data), mem_mask); +} + +/*------------------------------------------------- + + Bootleg Board for KOF96 + + Games: The King of Fighters 96, Sonic 3D Blast 6, Street + Fighter Zero 2 + + MMC3 clone + + iNES: mapper 187 + + In MESS: Preliminary Support. + + -------------------------------------------------*/ + +void nes_kof96_device::prg_cb(int start, int bank) +{ + if (!(m_reg[0] & 0x80)) + prg8_x(start, bank); +} + +void nes_kof96_device::chr_cb(int start, int bank, int source) +{ + UINT8 chr_page = (m_latch & 0x80) >> 5; + + if ((start & 0x04) == chr_page) + bank |= 0x100; + + chr1_x(start, bank, source); +} + +WRITE8_MEMBER(nes_kof96_device::write_l) +{ + UINT8 new_bank; + LOG_MMC(("kof96 write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset == 0x1000) + { + m_reg[0] = data; + + if (m_reg[0] & 0x80) + { + new_bank = (m_reg[0] & 0x1f); + + if (m_reg[0] & 0x20) + prg32(new_bank >> 2); + else + { + prg16_89ab(new_bank); + prg16_cdef(new_bank); + } + } + else + set_prg(m_prg_base, m_prg_mask); + } + + if (offset >= 0x1000) + { + switch (data & 0x03) + { + case 0x00: + case 0x01: + m_reg[1] = 0x83; + break; + case 0x02: + m_reg[1] = 0x42; + break; + case 0x03: + m_reg[1] = 0x00; + break; + } + + } + + if (!m_reg[3] && offset > 0x1000) + { + m_reg[3] = 1; + space.write_byte(0x4017, 0x40); + } +} + +READ8_MEMBER(nes_kof96_device::read_l) +{ + LOG_MMC(("kof96 read_l, offset: %04x\n", offset)); + offset += 0x100; + + if (!(offset < 0x1000)) + return m_reg[1]; + else + return 0; +} + +WRITE8_MEMBER(nes_kof96_device::write_h) +{ + LOG_MMC(("kof96 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6003) + { + case 0x0000: + m_reg[2] = 1; + txrom_write(space, 0x0000, data, mem_mask); + break; + + case 0x0001: + if (m_reg[2]) + txrom_write(space, 0x0001, data, mem_mask); + break; + + case 0x0002: + break; + + case 0x0003: + m_reg[2] = 0; + + if (data == 0x28) + prg8_cd(0x17); + else if (data == 0x2a) + prg8_ab(0x0f); + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + Bootleg Board for Super Fighter III + + MMC3 clone + + iNES: mapper 197 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_sf3_device::set_chr(UINT8 chr_source, int chr_base, int chr_mask) +{ + chr4_0(chr_base | ((m_mmc_vrom_bank[0] >> 1) & chr_mask), chr_source); + chr2_4(chr_base | (m_mmc_vrom_bank[1] & chr_mask), chr_source); + chr2_6(chr_base | (m_mmc_vrom_bank[2] & chr_mask), chr_source); +} + +WRITE8_MEMBER(nes_sf3_device::write_h) +{ + UINT8 cmd; + LOG_MMC(("sf3 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x0001: + cmd = m_latch & 0x07; + switch (cmd) + { + case 0: case 2: case 4: + m_mmc_vrom_bank[cmd >> 1] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + case 6: + case 7: + m_mmc_prg_bank[cmd - 6] = data; + set_prg(m_prg_base, m_prg_mask); + break; + } + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + Bootleg Board 37017 (?) by Gouder + + Games: Street Fighter IV + + MMC3 clone. It also requires reads from 0x5000-0x7fff. + + iNES: mapper 208 + + In MESS: Preliminary Support. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_gouder_device::write_l) +{ + static const UINT8 conv_table[256] = + { + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x49,0x19,0x09,0x59,0x49,0x19,0x09, + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x51,0x41,0x11,0x01,0x51,0x41,0x11,0x01, + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x49,0x19,0x09,0x59,0x49,0x19,0x09, + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x51,0x41,0x11,0x01,0x51,0x41,0x11,0x01, + 0x00,0x10,0x40,0x50,0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x18,0x48,0x58,0x08,0x18,0x48,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x10,0x40,0x50,0x00,0x10,0x40,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x18,0x48,0x58,0x08,0x18,0x48,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x58,0x48,0x18,0x08,0x58,0x48,0x18,0x08, + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x50,0x40,0x10,0x00,0x50,0x40,0x10,0x00, + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x58,0x48,0x18,0x08,0x58,0x48,0x18,0x08, + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x50,0x40,0x10,0x00,0x50,0x40,0x10,0x00, + 0x01,0x11,0x41,0x51,0x01,0x11,0x41,0x51,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x09,0x19,0x49,0x59,0x09,0x19,0x49,0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x11,0x41,0x51,0x01,0x11,0x41,0x51,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x09,0x19,0x49,0x59,0x09,0x19,0x49,0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + }; + + LOG_MMC(("gouder write_l, offset: %04x, data: %02x\n", offset, data)); + + if (!(offset < 0x1700)) + m_reg[offset & 0x03] = data ^ conv_table[m_reg[4]]; + else if (!(offset < 0xf00)) + m_reg[4] = data; + else if (!(offset < 0x700)) + prg32(((data >> 3) & 0x02) | (data & 0x01)); +} + +READ8_MEMBER(nes_gouder_device::read_l) +{ + LOG_MMC(("gouder read_l, offset: %04x\n", offset)); + + if (!(offset < 0x1700)) + return m_reg[offset & 0x03]; + + return 0x00; +} + +/* writes to 0x8000-0xffff are like MMC3 but no PRG bankswitch (beacuse it is handled by low writes) */ +void nes_gouder_device::prg_cb(int start, int bank) +{ + return; +} + + +/*------------------------------------------------- + + UNL-SA-9602B + + Sachen boards used for a chinese port of Princess Maker (?) + + in MESS: Very Preliminary support, based on Cah4e3 + code in FCEUMM + + -------------------------------------------------*/ + +void nes_sa9602b_device::prg_cb(int start, int bank) +{ + if (m_prg_chip == 3) + { + printf("Accessing PRG beyond end of ROM!\n"); + m_prg_chip = 2; // assume that the higher PRG chip is mirrored twice + } + prg8_x(start, (m_prg_chip * 0x40) | (bank & 0x3f)); + + if (m_latch & 0x40) + prg8_89(0x3e); + else + prg8_cd(0x3e); + + prg8_ef(0x3f); +// printf("start %d, bank %d\n", start, bank); +} + + +WRITE8_MEMBER( nes_sa9602b_device::write_h ) +{ + LOG_MMC(("sa9602b write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x0000: + m_reg = data; + break; + case 0x0001: + if ((m_reg & 7) < 6) + m_prg_chip = (data & 0xc0) >> 6; + set_prg(0, m_prg_mask); + break; + } + + txrom_write(space, offset, data, mem_mask); +} + +/*------------------------------------------------- + + UNL-SHERO + + Sachen boards used for Street Heroes + + in MESS: Very Preliminary support + + -------------------------------------------------*/ + + +void nes_sachen_shero_device::chr_cb( int start, int bank, int source ) +{ + int shift = start < 2 ? 5 : + start < 4 ? 6 : + start < 6 ? 8 : 7; + if (!BIT(m_reg, 6)) + chr1_x(start, ((m_reg << shift) & 0x100) | bank, source); +} + + +WRITE8_MEMBER( nes_sachen_shero_device::write_l ) +{ + LOG_MMC(("shero write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x4100; + + if (offset == 0x4100) + { + m_reg = data; + if (BIT(m_reg, 6)) + chr8(0, CHRRAM); + else + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } +} + +READ8_MEMBER( nes_sachen_shero_device::read_l ) +{ + LOG_MMC(("shero read_l, offset: %04x\n", offset)); + offset += 0x4100; + + if (offset == 0x4100) + { + // DSW read! + } + return m_open_bus; // open bus +} + + + +/*------------------------------------------------- + + MULTIGAME CARTS BASED ON MMC3 + + -------------------------------------------------*/ + + /*------------------------------------------------- + + Board BMC-FK23C + + MMC3 clone + + In MESS: partially supported (still to sort initial banking + for many games) + + -------------------------------------------------*/ + +#if 0 +// alt version... +void nes_fk23c_device::prg_cb(int start, int bank) +{ + if (((m_reg[0] & 0x07) - 3) > 1 && (!(m_reg[3] & 0x02) || start < 2)) + { + if (m_reg[0] & 0x03) + bank = (bank & (0x3f >> (m_reg[0] & 0x03))) | (m_reg[1] << 1); + + prg8_x(start, bank); + } +} + +void nes_fk23c_device::chr_cb(int start, int bank, int source) +{ + if (!(m_reg[0] & 0x40) && (!(m_reg[3] & 0x02) || (start != 1 && start != 3))) + chr1_x(start, ((m_reg[2] & 0x7f) << 3) | bank, source); +} + +#endif + +void nes_fk23c_device::prg_cb(int start, int bank) +{ + UINT8 mask = (0x3f >> (m_reg[0] & 0x03)); + + if ((m_reg[0] & 0x07) < 3) + { + if (!(m_reg[0] & 0x03)) + bank = (bank & mask) | ((m_reg[1] & (0x7f ^ mask)) << 1); + + prg8_x(start, bank); + } +} + +void nes_fk23c_device::chr_cb(int start, int bank, int source) +{ + if (!(m_reg[0] & 0x40) && (!(m_reg[3] & 0x02) || (start != 1 && start != 3))) + chr1_x(start, ((m_reg[2] & 0x7f) << 3) | bank, source); +} + +void nes_fk23c_device::fk23c_set_prg() +{ + if ((m_reg[0] & 0x07) == 4) + prg32((m_reg[1] & 0x7f) >> 1); + else if ((m_reg[0] & 0x07) == 3) + { + prg16_89ab(m_reg[1] & 0x7f); + prg16_cdef(m_reg[1] & 0x7f); + } + else + { + if (m_reg[3] & 0x02) + { + prg8_cd(m_reg[4]); + prg8_ef(m_reg[5]); + } + else + set_prg(m_prg_base, m_prg_mask); + } +} + +void nes_fk23c_device::fk23c_set_chr() +{ + if (m_reg[0] & 0x40) + chr8(m_reg[2] | m_mmc_cmd1, m_chr_source); + else + { + if (m_reg[3] & 0x02) + { + int base = (m_reg[2] & 0x7f) << 3; + chr1_x(1, base | m_reg[6], m_chr_source); + chr1_x(3, base | m_reg[7], m_chr_source); + } + else + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } +} + +WRITE8_MEMBER(nes_fk23c_device::write_l) +{ + LOG_MMC(("fk23c write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset >= 0x1000) + { + if (offset & (1 << 4)) // here it should be (4 + m_mmc_dipsetting) + { + m_reg[offset & 0x03] = data; + + fk23c_set_prg(); + fk23c_set_chr(); + } + } +} + +WRITE8_MEMBER(nes_fk23c_device::write_h) +{ + LOG_MMC(("fk23c write_h, offset: %04x, data: %02x\n", offset, data)); + + if (m_reg[0] & 0x40) + { + if (m_reg[0] & 0x30) + m_mmc_cmd1 = 0; + else + { + m_mmc_cmd1 = data & 0x03; + fk23c_set_chr(); + } + } + else + { + switch (offset & 0x6001) + { + case 0x0001: + if ((m_reg[3] & 0x02) && (m_latch & 0x08)) + { + m_reg[4 | (m_latch & 0x03)] = data; + fk23c_set_prg(); + fk23c_set_chr(); + } + else + txrom_write(space, offset, data, mem_mask); + break; + + case 0x2000: + set_nt_mirroring(data ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } + } +} + +/*------------------------------------------------- + + Board BMC-SUPER24IN1SC03 + + Games: Super 24-in-1 + + In MESS: Partially Supported + + -------------------------------------------------*/ + +void nes_s24in1sc03_device::prg_cb(int start, int bank) +{ + static const UINT8 masks[8] = {0x3f, 0x1f, 0x0f, 0x01, 0x03, 0x00, 0x00, 0x00}; + int prg_base = m_reg[1] << 1; + int prg_mask = masks[m_reg[0] & 0x07]; + + bank = prg_base | (bank & prg_mask); + prg8_x(start, bank); +} + +void nes_s24in1sc03_device::chr_cb(int start, int bank, int source) +{ + UINT8 chr = BIT(m_reg[0], 5) ? CHRRAM : CHRROM; + int chr_base = (m_reg[2] << 3) & 0xf00; + + chr1_x(start, chr_base | bank, chr); +} + +WRITE8_MEMBER(nes_s24in1sc03_device::write_l) +{ + LOG_MMC(("s24in1sc03 write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset == 0x1ff0) + { + m_reg[0] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + set_prg(m_prg_base, m_prg_mask); + } + + if (offset == 0x1ff1) + { + m_reg[1] = data; + set_prg(m_prg_base, m_prg_mask); + } + + if (offset == 0x1ff2) + { + m_reg[2] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } +} + +/*------------------------------------------------- + + BMC-15IN1 + + Unknown Bootleg Multigame Board + Games: 3 in 1, 15 in 1 + + iNES: mapper 205, MMC3 clone + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_15in1_device::write_m) +{ + LOG_MMC(("bmc_15in1 write_m, offset: %04x, data: %02x\n", offset, data)); + + if (offset & 0x0800) + { + m_prg_base = (data & 0x03) << 4; + m_prg_mask = (data & 0x02) ? 0x0f : 0x1f; + m_chr_base = (data & 0x03) << 7; + m_chr_mask = (data & 0x02) ? 0x7f : 0xff; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } +} + +/*------------------------------------------------- + + BMC-SUPERBIG-7IN1 + + Known Boards: Unknown Multigame Bootleg Board + Games: Kunio 8 in 1, Super Big 7 in 1 + + iNES: mapper 44 + + In MESS: Supported. It also uses mmc3_irq. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_sbig7_device::write_h) +{ + UINT8 page; + LOG_MMC(("bmc_sbig7 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x2001: /* $a001 - Select 128K ROM/VROM base (0..5) or last 256K ROM/VRAM base (6) */ + page = (data & 0x07); + if (page > 6) + page = 6; + + m_prg_base = page << 4; + m_prg_mask = (page > 5) ? 0x1f : 0x0f; + m_chr_base = page << 7; + m_chr_mask = (page > 5) ? 0xff : 0x7f; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + BMC-HIK8IN1 + + Known Boards: Unknown Multigame Bootleg Board + Games: Street Fighter V, various multigame carts + + iNES: mapper 45 + + In MESS: Supported. It also uses mmc3_irq. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_hik8_device::write_m) +{ + LOG_MMC(("bmc_hik8 write_m, offset: %04x, data: %02x\n", offset, data)); + + /* This bit is the "register lock". Once register are locked, writes go to WRAM + and there is no way to unlock them (except by resetting the machine) */ + if ((m_reg[3] & 0x40) && !m_prgram.empty()) + m_prgram[offset] = data; + else + { + m_reg[m_count] = data; + m_count = (m_count + 1) & 0x03; + + if (!m_count) + { + LOG_MMC(("bmc_hik8 write_m, command completed %02x %02x %02x %02x\n", m_reg[3], + m_reg[2], m_reg[1], m_reg[0])); + + m_prg_base = m_reg[1]; + m_prg_mask = 0x3f ^ (m_reg[3] & 0x3f); + m_chr_base = ((m_reg[2] & 0xf0) << 4) | m_reg[0]; + if (BIT(m_reg[2], 3)) + m_chr_mask = (1 << ((m_reg[2] & 7) + 1)) - 1; + else if (m_reg[2]) + m_chr_mask = 0; + else + m_chr_mask = 0xff; // i.e. we use the vrom_bank with no masking + + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } + } +} + +/*------------------------------------------------- + + BMC-SUPERHIK-4IN1 + + Known Boards: Unknown Multigame Bootleg Board + Games: Super HIK 4 in 1 + + iNES: mapper 49 + + In MESS: Supported. It also uses mmc3_irq. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_hik4_device::write_m) +{ + LOG_MMC(("bmc_hik4 write_m, offset: %04x, data: %02x\n", offset, data)); + + /* mid writes only work when WRAM is enabled. not sure if I should + change the condition to m_mmc_latch2==0x80 (i.e. what is the effect of + the read-only bit?) */ + if (m_wram_protect & 0x80) + { + if (data & 0x01) /* if this is 0, then we have 32k PRG blocks */ + { + m_prg_base = (data & 0xc0) >> 2; + m_prg_mask = 0x0f; + set_prg(m_prg_base, m_prg_mask); + } + else + prg32((data & 0x30) >> 4); + + m_chr_base = (data & 0xc0) << 1; + m_chr_mask = 0x7f; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } +} + +/*------------------------------------------------- + + BMC-MARIOPARTY-7IN1 + + Known Boards: Unknown Multigame Bootleg Board + Games: Mario 7 in 1 + + MMC3 clone + + iNES: mapper 52 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_mario7in1_device::write_m) +{ + UINT8 helper1, helper2; + LOG_MMC(("bmc_mario7in1 write_m, offset: %04x, data: %02x\n", offset, data)); + + /* mid writes only work when WRAM is enabled. not sure if I should + change the condition to m_map52_reg_written == 0x80 (i.e. what is the effect of + the read-only bit?) and it only can happen once! */ + if ((m_wram_protect & 0x80) && !m_reg_written) + { + helper1 = (data & 0x08); + helper2 = (data & 0x40); + + m_prg_base = helper1 ? ((data & 0x07) << 4) : ((data & 0x06) << 4); + m_prg_mask = helper1 ? 0x0f : 0x1f; + m_chr_base = ((data & 0x20) << 4) | ((data & 0x04) << 6) | (helper2 ? ((data & 0x10) << 3) : 0); + m_chr_mask = helper2 ? 0x7f : 0xff; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); + + m_reg_written = 1; + } + else + m_prgram[offset] = data; +} + +/*------------------------------------------------- + + BMC-GOLD-7IN1 + + Known Boards: Unknown Multigame Bootleg Board + Games: Super HIK Gold 7 in 1, Golden 7 in 1 and many more + + MMC3 clone, same as BMC-MARIOPARTY-7IN1 but with switched CHR + bank lines + + iNES: mapper 52 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_gold7in1_device::write_m) +{ + UINT8 helper1, helper2; + LOG_MMC(("bmc_gold7in1 write_m, offset: %04x, data: %02x\n", offset, data)); + + if ((m_wram_protect & 0x80) && !m_reg_written) + { + helper1 = (data & 0x08); + helper2 = (data & 0x40); + + m_prg_base = helper1 ? ((data & 0x07) << 4) : ((data & 0x06) << 4); + m_prg_mask = helper1 ? 0x0f : 0x1f; + m_chr_base = ((data & 0x20) << 3) | ((data & 0x04) << 7) | (helper2 ? ((data & 0x10) << 3) : 0); + m_chr_mask = helper2 ? 0x7f : 0xff; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); + + m_reg_written = BIT(data, 7); // mc_2hikg & mc_s3nt3 write here multiple time + } + else + m_prgram[offset] = data; +} + +/*------------------------------------------------- + + BMC-GOLDENCARD-6IN1 + + Known Boards: Unknown Bootleg Multigame Board + Games: Golden Card 6 in 1 + + MMC3 clone + + iNES: mapper 217 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_bmc_gc6in1_device::prg_cb(int start, int bank) +{ + if (m_reg[1] & 0x08) + bank &= 0x1f; + else + { + bank &= 0x0f; + bank |= m_reg[1] & 0x10; + } + + prg8_x(start, bank | ((m_reg[1] & 0x03) << 5)); +} + +void nes_bmc_gc6in1_device::chr_cb(int start, int bank, int source) +{ + if (!(m_reg[1] & 0x08)) + bank = ((m_reg[1] & 0x10) << 3) | (bank & 0x7f); + + + bank |= ((m_reg[1] << 6) & 0x100); + chr1_x(start, ((m_reg[1] & 0x03) << 8) | bank, source); +} + +WRITE8_MEMBER(nes_bmc_gc6in1_device::write_l) +{ + UINT8 bank; + LOG_MMC(("bmc_gc6in1 write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset == 0x1000) + { + m_reg[0] = data; + if (data & 0x80) + { + bank = (data & 0x0f) | ((m_reg[1] & 0x03) << 4); + prg16_89ab(bank); + prg16_cdef(bank); + } + else + set_prg(m_prg_base, m_prg_mask); + } + else if (offset == 0x1001) + { + m_reg[1] = data; + set_prg(m_prg_base, m_prg_mask); + } + else if (offset == 0x1007) + { + m_reg[2] = data; + } +} + +WRITE8_MEMBER(nes_bmc_gc6in1_device::write_h) +{ + UINT8 cmd; + static const UINT8 conv_table[8] = {0, 6, 3, 7, 5, 2, 4, 1}; + LOG_MMC(("bmc_gc6in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (!m_reg[2]) + { + // in this case we act like MMC3, only with alt prg/chr handlers + txrom_write(space, offset, data, mem_mask); + } + else + { + switch (offset & 0x6001) + { + case 0x0000: + txrom_write(space, 0x4000, data, mem_mask); + break; + + case 0x0001: + data = (data & 0xc0) | conv_table[data & 0x07]; + m_reg[3] = 1; + txrom_write(space, 0x0000, data, mem_mask); + break; + + case 0x2000: + cmd = m_latch & 0x07; + if (m_reg[3]) + { + m_reg[3] = 0; + switch (cmd) + { + case 0: case 1: // these do not need to be separated: we take care of them in set_chr! + case 2: case 3: case 4: case 5: + m_mmc_vrom_bank[cmd] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + case 6: + case 7: + m_mmc_prg_bank[cmd - 6] = data; + set_prg(m_prg_base, m_prg_mask); + break; + } + } + break; + + + case 0x2001: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } + } +} + +/*------------------------------------------------- + + BMC-411120C + + + MMC3 clone + + + In MESS: Very Preliminary Support + + -------------------------------------------------*/ + +void nes_bmc_411120c_device::prg_cb(int start, int bank) +{ + if (m_reg & 8) // & 0xc when DSW change (diff menu?) + prg32(((m_reg >> 4) & 3) | 0x0c); + else + prg8_x(start, (bank & 0x0f) | ((m_reg & 0x03) << 4)); +} + +void nes_bmc_411120c_device::chr_cb(int start, int bank, int source) +{ + chr1_x(start, bank | ((m_reg & 3) << 7), source); +} + +WRITE8_MEMBER(nes_bmc_411120c_device::write_m) +{ + LOG_MMC(("bmc_411120c write_m, offset: %04x, data: %02x\n", offset, data)); + + m_reg = data; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); +} + +/*------------------------------------------------- + + BMC-830118C + + + MMC3 clone + + + In MESS: Very Preliminary Support + + -------------------------------------------------*/ + +void nes_bmc_830118c_device::prg_cb(int start, int bank) +{ + if ((m_reg & 0x0c) != 0x0c) + prg8_x(start, (bank & 0x0f) | ((m_reg & 0x0c) << 2)); + else + { + if (start == 0) + { + prg8_89((bank & 0x0f) | ((m_reg & 0x0c) << 2)); + prg8_ab((bank & 0x0f) | 0x20); + } + else if (start == 2) + { + prg8_cd((bank & 0x0f) | ((m_reg & 0x0c) << 2)); + prg8_ef((bank & 0x0f) | 0x20); + } + } +} + +void nes_bmc_830118c_device::chr_cb(int start, int bank, int source) +{ + chr1_x(start, (bank & 0x7f) | ((m_reg & 0x0c) << 5), source); +} + +WRITE8_MEMBER(nes_bmc_830118c_device::write_m) +{ + LOG_MMC(("bmc_830118c write_m, offset: %04x, data: %02x\n", offset, data)); + + if (offset >= 0x800 && offset < 0x900) + { + m_reg = data; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } +} + +/*------------------------------------------------- + + BMC-POWERJOY + + -------------------------------------------------*/ + +void nes_pjoy84_device::prg_cb(int start, int bank) +{ + UINT8 flip = (m_latch & 0x40) ? 2 : 0; + + if (!(m_reg[3] & 0x03)) + prg8_x(start, bank); + else if (start == flip) + { + if ((m_reg[3] & 0x03) == 0x03) + prg32(bank >> 2); + else + { + prg16_89ab(bank >> 1); + prg16_cdef(bank >> 1); + } + } +} + +void nes_pjoy84_device::chr_cb(int start, int bank, int source) +{ + if (!(m_reg[3] & 0x10)) + chr1_x(start, bank, source); +} + +inline void nes_pjoy84_device::set_base_mask() +{ + m_prg_base = ((m_reg[0] & (0x06 | BIT(m_reg[0], 6))) << 4) | + (BIT(m_reg[0], 4) << 7); + + m_chr_base = ((~m_reg[0] << 0) & 0x080 & m_reg[2]) | + ((m_reg[0] << 4) & 0x080 & m_reg[0]) | + ((m_reg[0] << 3) & 0x100) | + ((m_reg[0] << 5) & 0x200); + + m_prg_mask = BIT(m_reg[0], 6) ? 0x0f : 0x1f; + m_chr_mask = BIT(m_reg[0], 7) ? 0x7f : 0xff; +} + +WRITE8_MEMBER(nes_pjoy84_device::write_m) +{ + LOG_MMC(("pjoy84 write_m, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x03) + { + case 0x00: + case 0x03: + if (m_reg[3] & 0x80) + return; // else we act as if offset & 3 = 1,2 + case 0x01: + case 0x02: + m_reg[offset & 0x03] = data; + set_base_mask(); + if (m_reg[3] & 0x10) + chr8((m_chr_base >> 3) | (m_reg[2] & 0x0f), m_chr_source); + else + set_chr(m_chr_source, m_chr_base, m_chr_mask); + set_prg(m_prg_base, m_prg_mask); + break; + } +} + +#ifdef UNUSED_FUNCTION +/*------------------------------------------------- + + UNL-A9746 + + + MMC3 clone + + + Preliminary emulation based on Cah4e3's code + No dump is available (yet) for this. + + -------------------------------------------------*/ + +void nes_a9746_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_a9746_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg[0] = 0; + m_reg[1] = 0; + m_reg[2] = 0; + mmc3_common_initialize(0x7f, 0xff, 0); +} + +void nes_a9746_device::update_banks(UINT8 value) +{ + UINT8 bank = BITSWAP8(value & 0x3c,7,6,0,1,2,3,4,5); + + switch (m_reg[0]) + { + case 0x26: prg8_89(bank); break; + case 0x25: prg8_ab(bank); break; + case 0x24: prg8_cd(bank); break; + case 0x23: prg8_ef(bank); break; + } + + switch (m_reg[1]) + { + case 0x08: case 0x0a: case 0x0c: case 0x0e: + case 0x10: case 0x12: case 0x14: case 0x16: + case 0x18: case 0x1a: case 0x1c: case 0x1e: + m_reg[2] = (value << 4); + break; + case 0x09: chr1_0(m_reg[2] | (value >> 1), m_chr_source); break; + case 0x0b: chr1_1(m_reg[2] | (value >> 1) | 1, m_chr_source); break; + case 0x0d: chr1_2(m_reg[2] | (value >> 1), m_chr_source); break; + case 0x0f: chr1_3(m_reg[2] | (value >> 1) | 1, m_chr_source); break; + case 0x11: chr1_4(m_reg[2] | (value >> 1), m_chr_source); break; + case 0x15: chr1_5(m_reg[2] | (value >> 1), m_chr_source); break; + case 0x19: chr1_6(m_reg[2] | (value >> 1), m_chr_source); break; + case 0x1d: chr1_7(m_reg[2] | (value >> 1), m_chr_source); break; + } +} + +WRITE8_MEMBER(nes_a9746_device::write_h) +{ + LOG_MMC(("unl_a9746 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6003) + { + case 0x0000: + m_reg[1] = data; + m_reg[0] = 0; + break; + case 0x0001: + update_banks(data); + break; + case 0x0002: + m_reg[0] = data; + m_reg[1] = 0; + break; + + case 0x0003: + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } +} +#endif diff --git a/src/devices/bus/nes/mmc3_clones.h b/src/devices/bus/nes/mmc3_clones.h new file mode 100644 index 00000000000..9445395889d --- /dev/null +++ b/src/devices/bus/nes/mmc3_clones.h @@ -0,0 +1,697 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_MMC3_CLONES_H +#define __NES_MMC3_CLONES_H + +#include "mmc3.h" + + +// ======================> nes_nitra_device + +class nes_nitra_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_nitra_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); +}; + + +// ======================> nes_ks7057_device + +class nes_ks7057_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_ks7057_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); +}; + + +// ======================> nes_sbros11_device + +class nes_sbros11_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_sbros11_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); +}; + + +// ======================> nes_malisb_device + +class nes_malisb_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_malisb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); +}; + + +// ======================> nes_family4646_device + +class nes_family4646_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_family4646_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + + +// ======================> nes_pikay2k_device + +class nes_pikay2k_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_pikay2k_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[2]; +}; + + +// ======================> nes_8237_device + +class nes_8237_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_8237_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[3]; + int m_cd_enable; +}; + + +// ======================> nes_sglionk_device + +class nes_sglionk_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_sglionk_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_reg; + int m_reg_enabled; +}; + + +// ======================> nes_sgboog_device + +class nes_sgboog_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_sgboog_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +private: + virtual void set_prg(int prg_base, int prg_mask); + UINT8 m_reg[3]; + UINT8 m_mode; +}; + + +// ======================> nes_kasing_device + +class nes_kasing_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_kasing_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual void prg_cb(int start, int bank); + + virtual void pcb_reset(); + +private: + UINT8 m_reg; +}; + + +// ======================> nes_kay_device + +class nes_kay_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_kay_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +private: + void update_regs(); + UINT8 m_reg[8]; + UINT8 m_low_reg; +}; + + +// ======================> nes_h2288_device + +class nes_h2288_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_h2288_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void prg_cb(int start, int bank); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[2]; // reg 1 is unused? +}; + + +// ======================> nes_6035052_device + +class nes_6035052_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_6035052_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_ex); + virtual DECLARE_WRITE8_MEMBER(write_ex); + virtual DECLARE_READ8_MEMBER(read_l) { return read_ex(space, offset, mem_mask); } + virtual DECLARE_READ8_MEMBER(read_m) { return read_ex(space, offset, mem_mask); } + virtual DECLARE_WRITE8_MEMBER(write_l) { write_ex(space, offset, data, mem_mask); } + virtual DECLARE_WRITE8_MEMBER(write_m) { write_ex(space, offset, data, mem_mask); } + + virtual void pcb_reset(); + +private: + UINT8 m_prot; +}; + + +// ======================> nes_txc_tw_device + +class nes_txc_tw_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_txc_tw_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_m) { write_l(space, offset & 0xff, data, mem_mask); } // offset does not really count for this mapper } + virtual void prg_cb(int start, int bank); +}; + + +// ======================> nes_kof97_device + +class nes_kof97_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_kof97_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); +}; + + +// ======================> nes_kof96_device + +class nes_kof96_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_kof96_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); // offset does not really count for this mapper + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[4]; +}; + + +// ======================> nes_sf3_device + +class nes_sf3_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_sf3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); + +protected: + virtual void set_chr(UINT8 chr, int chr_base, int chr_mask); +}; + + +// ======================> nes_gouder_device + +class nes_gouder_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_gouder_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual void prg_cb(int start, int bank); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[5]; +}; + + +// ======================> nes_sa9602b_device + +class nes_sa9602b_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_sa9602b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void prg_cb(int start, int bank); + + virtual void pcb_reset(); + +private: + UINT8 m_reg; + int m_prg_chip; +}; + + +// ======================> nes_sachen_shero_device + +class nes_sachen_shero_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_sachen_shero_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +private: + UINT8 m_reg; +}; + +#ifdef UNUSED_FUNCTION +// ======================> nes_a9746_device + +class nes_a9746_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_a9746_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + void update_banks(UINT8 value); + UINT8 m_reg[3]; +}; +#endif + + +// ======================> nes_fk23c_device + +class nes_fk23c_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_fk23c_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_fk23c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +protected: + void fk23c_set_prg(); + void fk23c_set_chr(); + UINT8 m_reg[8]; + UINT8 m_mmc_cmd1; +}; + + +// ======================> nes_fk23ca_device + +class nes_fk23ca_device : public nes_fk23c_device +{ +public: + // construction/destruction + nes_fk23ca_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual void pcb_reset(); +}; + + +// ======================> nes_s24in1sc03_device + +class nes_s24in1sc03_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_s24in1sc03_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[3]; +}; + + +// ======================> nes_bmc_15in1_device + +class nes_bmc_15in1_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_bmc_15in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_sbig7_device + +class nes_bmc_sbig7_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_bmc_sbig7_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_hik8_device + +class nes_bmc_hik8_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_bmc_hik8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[4]; + UINT8 m_count; +}; + + +// ======================> nes_bmc_hik4_device + +class nes_bmc_hik4_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_bmc_hik4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_mario7in1_device + +class nes_bmc_mario7in1_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_bmc_mario7in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); + +private: + UINT8 m_reg_written; +}; + + +// ======================> nes_bmc_gold7in1_device + +class nes_bmc_gold7in1_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_bmc_gold7in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); + +private: + UINT8 m_reg_written; +}; + + +// ======================> nes_bmc_gc6in1_device + +class nes_bmc_gc6in1_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_bmc_gc6in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[4]; +}; + + +// ======================> nes_bmc_411120c_device + +class nes_bmc_411120c_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_bmc_411120c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +private: + UINT8 m_reg; +}; + + +// ======================> nes_bmc_830118c_device + +class nes_bmc_830118c_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_bmc_830118c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +private: + UINT8 m_reg; +}; + + +// ======================> nes_pjoy84_device + +class nes_pjoy84_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_pjoy84_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +private: + inline void set_base_mask(); + UINT8 m_reg[4]; +}; + + + + +// device type definition +extern const device_type NES_NITRA; +extern const device_type NES_KS7057; +extern const device_type NES_SBROS11; +extern const device_type NES_MALISB; +extern const device_type NES_FAMILY4646; +extern const device_type NES_PIKAY2K; +extern const device_type NES_8237; +extern const device_type NES_SG_LIONK; +extern const device_type NES_SG_BOOG; +extern const device_type NES_KASING; +extern const device_type NES_KAY; +extern const device_type NES_H2288; +extern const device_type NES_6035052; +extern const device_type NES_TXC_TW; +extern const device_type NES_KOF97; +extern const device_type NES_KOF96; +extern const device_type NES_SF3; +extern const device_type NES_GOUDER; +extern const device_type NES_SA9602B; +extern const device_type NES_SACHEN_SHERO; +extern const device_type NES_A9746; + +extern const device_type NES_FK23C; +extern const device_type NES_FK23CA; +extern const device_type NES_S24IN1SC03; +extern const device_type NES_BMC_15IN1; +extern const device_type NES_BMC_SBIG7; +extern const device_type NES_BMC_HIK8; +extern const device_type NES_BMC_HIK4; +extern const device_type NES_BMC_MARIO7IN1; +extern const device_type NES_BMC_GOLD7IN1; +extern const device_type NES_BMC_GC6IN1; +extern const device_type NES_BMC_411120C; +extern const device_type NES_BMC_830118C; +extern const device_type NES_PJOY84; + +#endif diff --git a/src/devices/bus/nes/mmc5.c b/src/devices/bus/nes/mmc5.c new file mode 100644 index 00000000000..26149c48e50 --- /dev/null +++ b/src/devices/bus/nes/mmc5.c @@ -0,0 +1,679 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Nintendo MMC-5 / ExROM + + + Here we emulate the Nintendo MMC-5 / ExROM PCBs [mapper 5] + + + TODO: + - improve PPU code in order to support the two sets of registers used by MMC5 + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "mmc5.h" + +#include "cpu/m6502/m6502.h" +#include "video/ppu2c0x.h" // this has to be included so that IRQ functions can access PPU_BOTTOM_VISIBLE_SCANLINE +#include "sound/nes_apu.h" // temp hack to pass the additional sound regs to APU... + + +#ifdef NES_PCB_DEBUG +#define VERBOSE 1 +#else +#define VERBOSE 0 +#endif + +#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0) + + +#define LAST_CHR_REG_A 0 +#define LAST_CHR_REG_B 1 + +static const int m_mmc5_attrib[4] = {0x00, 0x55, 0xaa, 0xff}; + +//------------------------------------------------- +// constructor +//------------------------------------------------- + +const device_type NES_EXROM = &device_creator; + + +nes_exrom_device::nes_exrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_EXROM, "NES Cart ExROM (MMC-5) PCB", tag, owner, clock, "nes_exrom", __FILE__) +{ +} + + +void nes_exrom_device::device_start() +{ + common_start(); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_status)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_mult1)); + save_item(NAME(m_mult2)); + save_item(NAME(m_vrom_page_a)); + save_item(NAME(m_vrom_page_b)); + save_item(NAME(m_floodtile)); + save_item(NAME(m_floodattr)); + save_item(NAME(m_prg_mode)); + save_item(NAME(m_chr_mode)); + save_item(NAME(m_wram_base)); + save_item(NAME(m_wram_protect_1)); + save_item(NAME(m_wram_protect_2)); + save_item(NAME(m_vrom_bank)); + save_item(NAME(m_last_chr)); + save_item(NAME(m_ex1_chr)); + save_item(NAME(m_split_chr)); + save_item(NAME(m_prg_regs)); + save_item(NAME(m_prg_ram_mapped)); + save_item(NAME(m_ex1_bank)); + save_item(NAME(m_high_chr)); + save_item(NAME(m_split_scr)); + save_item(NAME(m_split_rev)); + save_item(NAME(m_split_ctrl)); + save_item(NAME(m_split_yst)); + save_item(NAME(m_split_bank)); + save_item(NAME(m_vcount)); + save_item(NAME(m_exram)); + save_item(NAME(m_ram_hi_banks)); + +} + +void nes_exrom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(m_prg_chunks - 2); + prg16_cdef(m_prg_chunks - 1); + chr8(0, m_chr_source); + + m_irq_count = 0; + m_irq_status = 0; + m_irq_enable = 0; + + m_mult1 = m_mult2 = 0; + m_vrom_page_a = m_vrom_page_b = 0; + + m_floodtile = m_floodattr = 0; + + m_prg_mode = 3; + m_chr_mode = 0; + m_wram_base = 0; + m_wram_protect_1 = 0; + m_wram_protect_2 = 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_last_chr = LAST_CHR_REG_A; + m_ex1_chr = 0; + m_split_chr = 0; + m_ex1_bank = 0; + m_vcount = 0; + + for (int i = 0; i < 12; i++) + m_vrom_bank[i] = 0x3ff; + + m_prg_regs[0] = 0xfc; + m_prg_regs[1] = 0xfd; + m_prg_regs[2] = 0xfe; + m_prg_regs[3] = 0xff; + m_prg_ram_mapped[0] = 0; + m_prg_ram_mapped[1] = 0; + m_prg_ram_mapped[2] = 0; + m_prg_ram_mapped[3] = 0; + + m_ram_hi_banks[0] = 0; + m_ram_hi_banks[1] = 0; + m_ram_hi_banks[2] = 0; + m_ram_hi_banks[3] = 0; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + ExROM (MMC5 based) board emulation + + Games: Castlevania III, Just Breed, many Koei titles + + iNES: mapper 5 + + MESS status: Partially supported + + -------------------------------------------------*/ + +void nes_exrom_device::update_prg() +{ + int bank0, bank1, bank2, bank3; + + switch (m_prg_mode) + { + case 0: // 32k banks + bank3 = m_prg_regs[3] >> 2; + prg32(bank3); + break; + + case 1: // 16k banks + bank1 = m_prg_regs[1] >> 1; + bank3 = m_prg_regs[3] >> 1; + + if (m_prg_ram_mapped[1]) + { + m_ram_hi_banks[0] = ((bank1 << 1) & 0x07); + m_ram_hi_banks[1] = ((bank1 << 1) & 0x07) | 1; + } + else + prg16_89ab(bank1); + + prg16_cdef(bank3); + break; + + case 2: // 16k-8k banks + bank1 = m_prg_regs[1] >> 1; + bank2 = m_prg_regs[2]; + bank3 = m_prg_regs[3]; + + if (m_prg_ram_mapped[1]) + { + m_ram_hi_banks[0] = ((bank1 << 1) & 0x07); + m_ram_hi_banks[1] = ((bank1 << 1) & 0x07) | 1; + } + else + prg16_89ab(bank1); + + if (m_prg_ram_mapped[2]) + m_ram_hi_banks[2] = (bank2 & 0x07); + else + prg8_cd(bank2); + + prg8_ef(bank3); + break; + + case 3: // 8k banks + bank0 = m_prg_regs[0]; + bank1 = m_prg_regs[1]; + bank2 = m_prg_regs[2]; + bank3 = m_prg_regs[3]; + + if (m_prg_ram_mapped[0]) + m_ram_hi_banks[0] = (bank0 & 0x07); + else + prg8_89(bank0); + + if (m_prg_ram_mapped[1]) + m_ram_hi_banks[1] = (bank1 & 0x07); + else + prg8_ab(bank1); + + if (m_prg_ram_mapped[2]) + m_ram_hi_banks[2] = (bank2 & 0x07); + else + prg8_cd(bank2); + + prg8_ef(bank3); + break; + } +} + +void nes_exrom_device::hblank_irq(int scanline, int vblank, int blanked ) +{ + m_vcount = scanline; + + if (scanline == m_irq_count) + { + if (m_irq_enable) + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + + m_irq_status = 0xff; + } + + // "In Frame" flag + if (scanline == 0) + m_irq_status |= 0x40; + else if (scanline > PPU_BOTTOM_VISIBLE_SCANLINE) + m_irq_status &= ~0x40; +} + + +void nes_exrom_device::set_mirror(int page, int src) +{ + switch (src) + { + case 0: + set_nt_page(page, CIRAM, 0, 1); + break; + case 1: + set_nt_page(page, CIRAM, 1, 1); + break; + case 2: + set_nt_page(page, EXRAM, 0, 1); + break; + case 3: + set_nt_page(page, MMC5FILL, 0, 0); + break; + default: + fatalerror("This should never happen\n"); + } +} + +inline bool nes_exrom_device::in_split() +{ + ppu2c0x_device *ppu = machine().device("ppu"); + int tile = ppu->get_tilenum(); + + if (tile < 34) + { + if (!m_split_rev && tile < m_split_ctrl) + return TRUE; + if (m_split_rev && tile >= m_split_ctrl) + return TRUE; + } + return FALSE; +} + +READ8_MEMBER(nes_exrom_device::nt_r) +{ + int page = ((offset & 0xc00) >> 10); + + switch (m_nt_src[page]) + { + 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()) + { + ppu2c0x_device *ppu = machine().device("ppu"); + int tile = ppu->get_tilenum(); + + 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 ((offset & 0x3ff) >= 0x3c0) + return m_mmc5_attrib[(m_exram[offset & 0x3ff] >> 6) & 0x03]; + else // in this case, we write Ex1 CHR bank, but then access NT normally! + { + m_ex1_chr = 1; + m_ex1_bank = (m_exram[offset & 0x3ff] & 0x3f) | (m_high_chr << 6); + } + } + return m_nt_access[page][offset & 0x3ff]; + } +} + +WRITE8_MEMBER(nes_exrom_device::nt_w) +{ + int page = ((offset & 0xc00) >> 10); + + if (!m_nt_writable[page]) + return; + + switch (m_nt_src[page]) + { + case EXRAM: + m_exram[offset & 0x3ff] = data; + break; + + case CIRAM: + default: + m_nt_access[page][offset & 0x3ff] = data; + break; + } +} + +inline UINT8 nes_exrom_device::base_chr_r(int bank, UINT32 offset) +{ + UINT32 helper = 0; + + switch (m_chr_mode) + { + case 0: + if (bank < 8) + helper = ((m_vrom_bank[bank | 7] & 0xff) * 0x2000) + (offset & 0x1fff); + else + helper = ((m_vrom_bank[bank | 3] & 0xff) * 0x2000) + (offset & 0xfff); + break; + case 1: + helper = ((m_vrom_bank[bank | 3] & 0xff) * 0x1000) + (offset & 0xfff); + break; + case 2: + helper = (m_vrom_bank[bank | 1] * 0x800) + (offset & 0x7ff); + break; + case 3: + helper = (m_vrom_bank[bank] * 0x400) + (offset & 0x3ff); + break; + } + + return m_vrom[helper & (m_vrom_size - 1)]; +} + +inline UINT8 nes_exrom_device::split_chr_r(UINT32 offset) +{ + UINT32 helper = (m_split_bank * 0x1000) + (offset & 0x3f8) + (m_split_yst & 7); + return m_vrom[helper & (m_vrom_size - 1)]; +} + +inline UINT8 nes_exrom_device::bg_ex1_chr_r(UINT32 offset) +{ + UINT32 helper = (m_ex1_bank * 0x1000) + (offset & 0xfff); + return m_vrom[helper & (m_vrom_size - 1)]; +} + +READ8_MEMBER(nes_exrom_device::chr_r) +{ + int bank = offset >> 10; + ppu2c0x_device *ppu = machine().device("ppu"); + + // Extended Attribute Mode (Ex1) does affect BG drawing even for 8x16 sprites (JustBreed uses it extensively!) + // However, if a game enables Ex1 but does not write a new m_ex1_bank, I'm not sure here we get the correct behavior + if (m_exram_control == 1 && ppu->get_draw_phase() == PPU_DRAW_BG && m_ex1_chr) + return bg_ex1_chr_r(offset & 0xfff); + + if (m_split_scr && !(m_exram_control & 0x02) && in_split() && ppu->get_draw_phase() == PPU_DRAW_BG && m_split_chr) + return split_chr_r(offset & 0xfff); + + if (ppu->is_sprite_8x16()) + { + if (ppu->get_draw_phase() == PPU_DRAW_OAM) + return base_chr_r(bank & 7, offset & 0x1fff); + + if (ppu->get_draw_phase() == PPU_DRAW_BG) + return base_chr_r((bank & 3) + 8, offset & 0x1fff); + } + + if (m_last_chr == LAST_CHR_REG_A) + return base_chr_r(bank & 7, offset & 0x1fff); + else + return base_chr_r((bank & 3) + 8, offset & 0x1fff); +} + + +READ8_MEMBER(nes_exrom_device::read_l) +{ + int value; + LOG_MMC(("exrom read_l, offset: %04x\n", offset)); + offset += 0x100; + + if ((offset >= 0x1c00) && (offset <= 0x1fff)) + { + // EXRAM + if (BIT(m_exram_control, 1)) // Modes 2,3 = read + return m_exram[offset - 0x1c00]; + else + return m_open_bus; // Modes 0,1 = open bus + } + + switch (offset) + { + case 0x1204: + value = m_irq_status; + m_irq_status &= ~0x80; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + return value; + + case 0x1205: + return (m_mult1 * m_mult2) & 0xff; + case 0x1206: + return ((m_mult1 * m_mult2) & 0xff00) >> 8; + + default: + logerror("MMC5 uncaught read, offset: %04x\n", offset + 0x4100); + return m_open_bus; + } +} + + +WRITE8_MEMBER(nes_exrom_device::write_l) +{ + LOG_MMC(("exrom write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if ((offset >= 0x1000) && (offset <= 0x1015)) + { + // SOUND + nesapu_device *m_sound = machine().device("nessound"); + m_sound->write(space, offset & 0x1f, data); + return; + } + + if ((offset >= 0x1c00) && (offset <= 0x1fff)) + { + // EXRAM + if (m_exram_control == 0x02) // Mode 2 = write data + m_exram[offset - 0x1c00] = data; + else if (m_exram_control != 0x03) // Modes 0,1 = write data in frame / write 0 otherwise + { + if (m_irq_status & 0x40) + m_exram[offset - 0x1c00] = data; + else + m_exram[offset - 0x1c00] = 0x00; + } + // Mode 3 = read only + return; + } + + switch (offset) + { + case 0x1100: + m_prg_mode = data & 0x03; + update_prg(); + //LOG_MMC(("MMC5 rom bank mode: %02x\n", data)); + break; + + case 0x1101: + m_chr_mode = data & 0x03; + m_ex1_chr = 0; + m_split_chr = 0; + //LOG_MMC(("MMC5 vrom bank mode: %02x\n", data)); + break; + + case 0x1102: + m_wram_protect_1 = data & 0x03; + LOG_MMC(("MMC5 vram protect 1: %02x\n", data)); + break; + case 0x1103: + m_wram_protect_2 = data & 0x03; + LOG_MMC(("MMC5 vram protect 2: %02x\n", data)); + break; + + case 0x1104: // Extra VRAM (EXRAM) + m_exram_control = data & 0x03; + LOG_MMC(("MMC5 exram control: %02x\n", data)); + break; + + case 0x1105: + set_mirror(0, (data & 0x03) >> 0); + set_mirror(1, (data & 0x0c) >> 2); + set_mirror(2, (data & 0x30) >> 4); + set_mirror(3, (data & 0xc0) >> 6); + break; + + case 0x1106: + m_floodtile = data; + break; + + case 0x1107: + m_floodattr = m_mmc5_attrib[data & 3]; + break; + + case 0x1113: + LOG_MMC(("MMC5 mid RAM bank select: %02x\n", data & 0x07)); + m_wram_base = data & 0x07; + break; + + + case 0x1114: + case 0x1115: + case 0x1116: + case 0x1117: + 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! + update_prg(); + break; + + case 0x1120: + case 0x1121: + case 0x1122: + case 0x1123: + case 0x1124: + case 0x1125: + case 0x1126: + case 0x1127: + m_vrom_bank[offset & 0x07] = data | (m_high_chr << 8); + m_last_chr = LAST_CHR_REG_A; + m_ex1_chr = 0; + m_split_chr = 0; + break; + + case 0x1128: + case 0x1129: + case 0x112a: + case 0x112b: + m_vrom_bank[offset & 0x0f] = data | (m_high_chr << 8); + m_last_chr = LAST_CHR_REG_B; + m_ex1_chr = 0; + m_split_chr = 0; + break; + + case 0x1130: + m_high_chr = data & 0x03; + m_ex1_chr = 0; + m_split_chr = 0; + 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 0x1201: + m_split_yst = (data >= 240) ? data - 16 : data; + break; + + case 0x1202: + m_split_bank = data; + m_split_chr = 1; + break; + + case 0x1203: + m_irq_count = data; + LOG_MMC(("MMC5 irq scanline: %d\n", m_irq_count)); + 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: + logerror("MMC5 uncaught write, offset: %04x, data: %02x\n", offset + 0x4100, data); + break; + } +} + +// 3bits are used to access the "WRAM" banks +// bit3 select the chip (2 of them can be accessed, each up to 32KB) +// bit1 & bit2 select the 8KB banks inside the chip +// same mechanism is used also when "WRAM" is mapped in higher banks +READ8_MEMBER(nes_exrom_device::read_m) +{ + LOG_MMC(("exrom read_m, offset: %04x\n", offset)); + if (!m_battery.empty() && !m_prgram.empty()) // 2 chips present: first is BWRAM, second is WRAM + { + if (m_wram_base & 0x04) + return m_prgram[(offset + (m_wram_base & 0x03) * 0x2000) & (m_prgram.size() - 1)]; + else + return m_battery[(offset + (m_wram_base & 0x03) * 0x2000) & (m_battery.size() - 1)]; + } + else if (!m_prgram.empty()) // 1 chip, WRAM + return m_prgram[(offset + (m_wram_base & 0x03) * 0x2000) & (m_prgram.size() - 1)]; + else if (!m_battery.empty()) // 1 chip, BWRAM + return m_battery[(offset + (m_wram_base & 0x03) * 0x2000) & (m_battery.size() - 1)]; + else + return m_open_bus; +} + +WRITE8_MEMBER(nes_exrom_device::write_m) +{ + LOG_MMC(("exrom write_m, offset: %04x, data: %02x\n", offset, data)); + if (m_wram_protect_1 != 0x02 || m_wram_protect_2 != 0x01) + return; + + if (!m_battery.empty() && m_wram_base < 4) + m_battery[(offset + m_wram_base * 0x2000) & (m_battery.size() - 1)] = data; + else if (!m_prgram.empty()) + m_prgram[(offset + (m_wram_base & 0x03) * 0x2000) & (m_prgram.size() - 1)] = data; +} + +// some games (e.g. Bandit Kings of Ancient China) write to PRG-RAM through 0x8000-0xdfff +READ8_MEMBER(nes_exrom_device::read_h) +{ + LOG_MMC(("exrom read_h, offset: %04x\n", offset)); + int bank = offset / 0x2000; + + if (bank < 3 && offset >= bank * 0x2000 && offset < (bank + 1) * 0x2000 && m_prg_ram_mapped[bank]) + { + if (!m_battery.empty() && m_ram_hi_banks[bank] < 4) + return m_battery[((m_ram_hi_banks[bank] * 0x2000) + (offset & 0x1fff)) & (m_battery.size() - 1)]; + else if (!m_prgram.empty()) + return m_prgram[(((m_ram_hi_banks[bank] & 3) * 0x2000) + (offset & 0x1fff)) & (m_prgram.size() - 1)]; + } + + return hi_access_rom(offset); +} + +WRITE8_MEMBER(nes_exrom_device::write_h) +{ + LOG_MMC(("exrom write_h, offset: %04x, data: %02x\n", offset, data)); + int bank = offset / 0x2000; + if (m_wram_protect_1 != 0x02 || m_wram_protect_2 != 0x01 || bank == 3 || !m_prg_ram_mapped[bank]) + return; + + if (!m_battery.empty() && m_ram_hi_banks[bank] < 4) + m_battery[((m_ram_hi_banks[bank] * 0x2000) + (offset & 0x1fff)) & (m_battery.size() - 1)] = data; + else if (!m_prgram.empty()) + m_prgram[(((m_ram_hi_banks[bank] & 3) * 0x2000) + (offset & 0x1fff)) & (m_prgram.size() - 1)] = data; +} diff --git a/src/devices/bus/nes/mmc5.h b/src/devices/bus/nes/mmc5.h new file mode 100644 index 00000000000..46fe19af7a8 --- /dev/null +++ b/src/devices/bus/nes/mmc5.h @@ -0,0 +1,94 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_MMC5_H +#define __NES_MMC5_H + +#include "nxrom.h" + + +// ======================> nes_exrom_device + +class nes_exrom_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_exrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual DECLARE_READ8_MEMBER(chr_r); + virtual DECLARE_READ8_MEMBER(nt_r); + virtual DECLARE_WRITE8_MEMBER(nt_w); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +protected: + void set_mirror(int page, int src); + void update_render_mode(); + void update_prg(); + + inline UINT8 base_chr_r(int bank, UINT32 offset); + inline UINT8 split_chr_r(UINT32 offset); + inline UINT8 bg_ex1_chr_r(UINT32 offset); + inline bool in_split(); + + UINT16 m_irq_count; + UINT8 m_irq_status; + int m_irq_enable; + + int m_mult1, m_mult2; + + int m_mmc5_scanline; + int m_vrom_page_a; + int m_vrom_page_b; + UINT16 m_vrom_bank[12]; // MMC5 has 10bit wide VROM regs! + + int m_floodtile; + int m_floodattr; + + int m_prg_mode; // $5100 + int m_chr_mode; // $5101 + int m_wram_protect_1; // $5102 + int m_wram_protect_2; // $5103 + int m_exram_control; // $5104 + int m_wram_base; // $5113 + + UINT8 m_last_chr; + UINT8 m_ex1_chr; + UINT8 m_split_chr; + UINT8 m_prg_regs[4]; + UINT8 m_prg_ram_mapped[4]; + + UINT8 m_ex1_bank; + + UINT8 m_high_chr; // $5130 + + UINT8 m_split_scr; // $5200 + UINT8 m_split_rev; // $5200 + UINT8 m_split_ctrl; // $5200 + UINT8 m_split_yst; // $5201 + UINT8 m_split_bank; // $5202 + int m_vcount; + + // MMC-5 contains 1K of internal ram + UINT8 m_exram[0x400]; + + UINT8 m_ram_hi_banks[4]; + + // int m_nes_vram_sprite[8]; +}; + + + +// device type definition +extern const device_type NES_EXROM; + +#endif diff --git a/src/devices/bus/nes/multigame.c b/src/devices/bus/nes/multigame.c new file mode 100644 index 00000000000..ba65c3d0e7c --- /dev/null +++ b/src/devices/bus/nes/multigame.c @@ -0,0 +1,2602 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Multigame Carts PCBs + + + Here we emulate several PCBs used in multigame pirate carts (not MMC-3 based) + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "multigame.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_ACTION52 = &device_creator; +const device_type NES_CALTRON6IN1 = &device_creator; +const device_type NES_RUMBLESTATION = &device_creator; +const device_type NES_SVISION16 = &device_creator; +const device_type NES_N625092 = &device_creator; +const device_type NES_A65AS = &device_creator; +const device_type NES_T262 = &device_creator; +const device_type NES_NOVEL1 = &device_creator; +const device_type NES_NOVEL2 = &device_creator; +const device_type NES_STUDYNGAME = &device_creator; +const device_type NES_SUPERGUN20IN1 = &device_creator; +const device_type NES_VT5201 = &device_creator; +const device_type NES_810544C = &device_creator; +const device_type NES_NTD03 = &device_creator; +const device_type NES_BMC_GB63 = &device_creator; +const device_type NES_BMC_GKA = &device_creator; +const device_type NES_BMC_GKB = &device_creator; +const device_type NES_BMC_WS = &device_creator; +const device_type NES_BMC_11160 = &device_creator; +const device_type NES_BMC_G146 = &device_creator; +const device_type NES_BMC_8157 = &device_creator; +const device_type NES_BMC_HIK300 = &device_creator; +const device_type NES_BMC_S700 = &device_creator; +const device_type NES_BMC_BALL11 = &device_creator; +const device_type NES_BMC_22GAMES = &device_creator; +const device_type NES_BMC_64Y2K = &device_creator; +const device_type NES_BMC_12IN1 = &device_creator; +const device_type NES_BMC_20IN1 = &device_creator; +const device_type NES_BMC_21IN1 = &device_creator; +const device_type NES_BMC_31IN1 = &device_creator; +const device_type NES_BMC_35IN1 = &device_creator; +const device_type NES_BMC_36IN1 = &device_creator; +const device_type NES_BMC_64IN1 = &device_creator; +const device_type NES_BMC_70IN1 = &device_creator; +const device_type NES_BMC_72IN1 = &device_creator; +const device_type NES_BMC_76IN1 = &device_creator; +const device_type NES_BMC_110IN1 = &device_creator; +const device_type NES_BMC_150IN1 = &device_creator; +const device_type NES_BMC_190IN1 = &device_creator; +const device_type NES_BMC_800IN1 = &device_creator; +const device_type NES_BMC_1200IN1 = &device_creator; +const device_type NES_BMC_GOLD150 = &device_creator; +const device_type NES_BMC_GOLD260 = &device_creator; +const device_type NES_BMC_CH001 = &device_creator; +const device_type NES_BMC_SUPER22 = &device_creator; +const device_type NES_BMC_4IN1RESET = &device_creator; +const device_type NES_BMC_42IN1RESET = &device_creator; + + +nes_action52_device::nes_action52_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_ACTION52, "NES Cart Action 52 PCB", tag, owner, clock, "nes_action52", __FILE__) +{ +} + +nes_caltron_device::nes_caltron_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_CALTRON6IN1, "NES Cart Caltron 6 in 1 PCB", tag, owner, clock, "nes_caltron", __FILE__) +{ +} + +nes_rumblestat_device::nes_rumblestat_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_RUMBLESTATION, "NES Cart Rumblestation PCB", tag, owner, clock, "nes_rumblestat", __FILE__) +{ +} + +nes_svision16_device::nes_svision16_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SVISION16, "NES Cart Supervision 16 in 1 PCB", tag, owner, clock, "nes_svision16", __FILE__) +{ +} + +nes_n625092_device::nes_n625092_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_N625092, "NES Cart N625092 PCB", tag, owner, clock, "nes_n625092", __FILE__) +{ +} + +nes_a65as_device::nes_a65as_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_A65AS, "NES Cart A65AS PCB", tag, owner, clock, "nes_a65as", __FILE__) +{ +} + +nes_t262_device::nes_t262_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_T262, "NES Cart T-262 PCB", tag, owner, clock, "nes_t262", __FILE__) +{ +} + +nes_novel1_device::nes_novel1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NOVEL1, "NES Cart Novel Diamond Type 1 PCB", tag, owner, clock, "nes_novel1", __FILE__) +{ +} + +nes_novel2_device::nes_novel2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NOVEL2, "NES Cart Novel Diamond Type 2 PCB", tag, owner, clock, "nes_novel2", __FILE__) +{ +} + +nes_studyngame_device::nes_studyngame_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_STUDYNGAME, "NES Cart Study n Game PCB", tag, owner, clock, "nes_studyngame", __FILE__) +{ +} + +nes_sgun20in1_device::nes_sgun20in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SUPERGUN20IN1, "NES Cart Supergun 20 in 1 PCB", tag, owner, clock, "nes_sgun20in1", __FILE__) +{ +} + +nes_vt5201_device::nes_vt5201_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_VT5201, "NES Cart VT5201 PCB", tag, owner, clock, "nes_vt5201", __FILE__) +{ +} + +nes_810544c_device::nes_810544c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_810544C, "NES Cart 810544-C-A1 PCB", tag, owner, clock, "nes_810544c", __FILE__) +{ +} + +nes_ntd03_device::nes_ntd03_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NTD03, "NES Cart NTD-03 PCB", tag, owner, clock, "nes_ntd03", __FILE__) +{ +} + +nes_bmc_gb63_device::nes_bmc_gb63_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_GB63, "NES Cart BMC Ghostbusters 63 in 1 PCB", tag, owner, clock, "nes_bmc_gb63", __FILE__) +{ +} + +nes_bmc_gka_device::nes_bmc_gka_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_GKA, "NES Cart BMC GK-A PCB", tag, owner, clock, "nes_bmc_gka", __FILE__) +{ +} + +nes_bmc_gkb_device::nes_bmc_gkb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_GKB, "NES Cart BMC GK-B PCB", tag, owner, clock, "nes_bmc_gkb", __FILE__) +{ +} + +nes_bmc_ws_device::nes_bmc_ws_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_WS, "NES Cart BMC WS PCB", tag, owner, clock, "nes_bmc_ws", __FILE__) +{ +} + +nes_bmc_11160_device::nes_bmc_11160_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_11160, "NES Cart BMC-11160 PCB", tag, owner, clock, "nes_bmc_11160", __FILE__) +{ +} + +nes_bmc_g146_device::nes_bmc_g146_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_G146, "NES Cart BMC-G-146 PCB", tag, owner, clock, "nes_bmc_g146", __FILE__) +{ +} + +nes_bmc_8157_device::nes_bmc_8157_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_8157, "NES Cart BMC-8157 PCB", tag, owner, clock, "nes_bmc_8157", __FILE__) +{ +} + +nes_bmc_hik300_device::nes_bmc_hik300_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_HIK300, "NES Cart BMC HIK 300 in 1 PCB", tag, owner, clock, "nes_bmc_hik300", __FILE__) +{ +} + +nes_bmc_s700_device::nes_bmc_s700_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_S700, "NES Cart BMC Super 700 in 1 PCB", tag, owner, clock, "nes_bmc_s700", __FILE__) +{ +} + +nes_bmc_ball11_device::nes_bmc_ball11_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_BALL11, "NES Cart BMC Ball 11 in 1 PCB", tag, owner, clock, "nes_bmc_ball11", __FILE__) +{ +} + +nes_bmc_22games_device::nes_bmc_22games_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_22GAMES, "NES Cart BMC 22 Games PCB", tag, owner, clock, "nes_bmc_22games", __FILE__) +{ +} + +nes_bmc_64y2k_device::nes_bmc_64y2k_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_64Y2K, "NES Cart BMC 64 in 1 Y2K PCB", tag, owner, clock, "nes_bmc_64y2k", __FILE__) +{ +} + +nes_bmc_12in1_device::nes_bmc_12in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_12IN1, "NES Cart BMC 12 in 1 PCB", tag, owner, clock, "nes_bmc_12in1", __FILE__) +{ +} + +nes_bmc_20in1_device::nes_bmc_20in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_20IN1, "NES Cart BMC 20 in 1 PCB", tag, owner, clock, "nes_bmc_20in1", __FILE__) +{ +} + +nes_bmc_21in1_device::nes_bmc_21in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_21IN1, "NES Cart BMC 21 in 1 PCB", tag, owner, clock, "nes_bmc_21in1", __FILE__) +{ +} + +nes_bmc_31in1_device::nes_bmc_31in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_31IN1, "NES Cart BMC 31 in 1 PCB", tag, owner, clock, "nes_bmc_31in1", __FILE__) +{ +} + +nes_bmc_35in1_device::nes_bmc_35in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_35IN1, "NES Cart BMC 35 in 1 PCB", tag, owner, clock, "nes_bmc_35in1", __FILE__) +{ +} + +nes_bmc_36in1_device::nes_bmc_36in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_36IN1, "NES Cart BMC 36 in 1 PCB", tag, owner, clock, "nes_bmc_36in1", __FILE__) +{ +} + +nes_bmc_64in1_device::nes_bmc_64in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_64IN1, "NES Cart BMC 64 in 1 PCB", tag, owner, clock, "nes_bmc_64in1", __FILE__) +{ +} + +nes_bmc_70in1_device::nes_bmc_70in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_70IN1, "NES Cart BMC 70 in 1 PCB", tag, owner, clock, "nes_bmc_70in1", __FILE__) +{ +} + +nes_bmc_72in1_device::nes_bmc_72in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_72IN1, "NES Cart BMC 72 in 1 PCB", tag, owner, clock, "nes_bmc_72in1", __FILE__) +{ +} + +nes_bmc_76in1_device::nes_bmc_76in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_76IN1, "NES Cart BMC 76 in 1 PCB", tag, owner, clock, "nes_bmc_76in1", __FILE__) +{ +} + +nes_bmc_110in1_device::nes_bmc_110in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_110IN1, "NES Cart BMC 110 in 1 PCB", tag, owner, clock, "nes_bmc_110in1", __FILE__) +{ +} + +nes_bmc_150in1_device::nes_bmc_150in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_150IN1, "NES Cart BMC 150 in 1 PCB", tag, owner, clock, "nes_bmc_150in1", __FILE__) +{ +} + +nes_bmc_190in1_device::nes_bmc_190in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_190IN1, "NES Cart BMC 190 in 1 PCB", tag, owner, clock, "nes_bmc_190in1", __FILE__) +{ +} + +nes_bmc_800in1_device::nes_bmc_800in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_800IN1, "NES Cart BMC 800 in 1 PCB", tag, owner, clock, "nes_bmc_800in1", __FILE__) +{ +} + +nes_bmc_1200in1_device::nes_bmc_1200in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_1200IN1, "NES Cart BMC 1200 in 1 PCB", tag, owner, clock, "nes_bmc_1200in1", __FILE__) +{ +} + +nes_bmc_gold150_device::nes_bmc_gold150_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_GOLD150, "NES Cart BMC Golden 150 in 1 PCB", tag, owner, clock, "nes_bmc_gold150", __FILE__) +{ +} + +nes_bmc_gold260_device::nes_bmc_gold260_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_GOLD260, "NES Cart BMC Golden 260 in 1 PCB", tag, owner, clock, "nes_bmc_gold260", __FILE__) +{ +} + +nes_bmc_ch001_device::nes_bmc_ch001_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_CH001, "NES Cart BMC CH-001 PCB", tag, owner, clock, "nes_bmc_ch001", __FILE__) +{ +} + +nes_bmc_super22_device::nes_bmc_super22_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_SUPER22, "NES Cart BMC Super 22 Games PCB", tag, owner, clock, "nes_bmc_super22", __FILE__) +{ +} + +nes_bmc_4in1reset_device::nes_bmc_4in1reset_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_4IN1RESET, "NES Cart BMC 4 in 1 (Reset Based) PCB", tag, owner, clock, "nes_bmc_4in1reset", __FILE__) +{ +} + +nes_bmc_42in1reset_device::nes_bmc_42in1reset_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BMC_42IN1RESET, "NES Cart BMC 42 in 1 (Reset Based) PCB", tag, owner, clock, "nes_bmc_42in1reset", __FILE__) +{ +} + + + + +void nes_action52_device::device_start() +{ + common_start(); +} + +void nes_action52_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_caltron_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_caltron_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_latch = 0; +} + +void nes_rumblestat_device::device_start() +{ + common_start(); + save_item(NAME(m_prg)); + save_item(NAME(m_chr)); +} + +void nes_rumblestat_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_prg = 0; + m_chr = 0; +} + +void nes_svision16_device::device_start() +{ + common_start(); + save_item(NAME(m_latch1)); + save_item(NAME(m_latch2)); +} + +void nes_svision16_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_latch1 = 0; + m_latch2 = 0; +} + +void nes_n625092_device::device_start() +{ + common_start(); + save_item(NAME(m_latch1)); + save_item(NAME(m_latch2)); +} + +void nes_n625092_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0); + chr8(0, m_chr_source); + + m_latch1 = 0; + m_latch2 = 0; +} + +void nes_a65as_device::device_start() +{ + common_start(); +} + +void nes_a65as_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(7); + set_nt_mirroring(PPU_MIRROR_VERT); + chr8(0, m_chr_source); +} + +void nes_t262_device::device_start() +{ + common_start(); + save_item(NAME(m_latch1)); + save_item(NAME(m_latch2)); +} + +void nes_t262_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(7); + chr8(0, m_chr_source); + + m_latch1 = 0; + m_latch2 = 0; +} + +void nes_novel1_device::device_start() +{ + common_start(); +} + +void nes_novel1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + set_nt_mirroring(PPU_MIRROR_VERT); +} + +void nes_novel2_device::device_start() +{ + common_start(); +} + +void nes_novel2_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_studyngame_device::device_start() +{ + common_start(); +} + +void nes_studyngame_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_sgun20in1_device::device_start() +{ + common_start(); +} + +void nes_sgun20in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0); + chr8(0, m_chr_source); +} + +void nes_vt5201_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + save_item(NAME(m_dipsetting)); +} + +void nes_vt5201_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_latch = 0; + m_dipsetting = 0; +} + +void nes_810544c_device::device_start() +{ + common_start(); +} + +void nes_810544c_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0); + chr8(0, m_chr_source); + set_nt_mirroring(PPU_MIRROR_VERT); +} + +void nes_ntd03_device::device_start() +{ + common_start(); +} + +void nes_ntd03_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_bmc_gb63_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + save_item(NAME(m_dipsetting)); + save_item(NAME(m_reg)); + save_item(NAME(m_vram_disable)); +} + +void nes_bmc_gb63_device::pcb_reset() +{ + prg16_89ab(0); + prg16_cdef(0xfff); + chr8(0, CHRRAM); + + m_latch = 0; + m_dipsetting = 0; + m_reg[0] = 0; + m_reg[1] = 0; + update_banks(); + m_vram_disable = 0; +} + +void nes_bmc_gka_device::device_start() +{ + common_start(); + save_item(NAME(m_latch1)); + save_item(NAME(m_latch2)); +} + +void nes_bmc_gka_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0); + chr8(0, m_chr_source); + + m_latch1 = 0; + m_latch2 = 0; +} + +void nes_bmc_gkb_device::device_start() +{ + common_start(); +} + +void nes_bmc_gkb_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_bmc_ws_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_bmc_ws_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_latch = 0; +} + +void nes_bmc_11160_device::device_start() +{ + common_start(); +} + +void nes_bmc_11160_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_bmc_g146_device::device_start() +{ + common_start(); +} + +void nes_bmc_g146_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_bmc_8157_device::device_start() +{ + common_start(); +} + +void nes_bmc_8157_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_bmc_hik300_device::device_start() +{ + common_start(); +} + +void nes_bmc_hik300_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0xff); + chr8(0xff, CHRROM); +} + +void nes_bmc_s700_device::device_start() +{ + common_start(); +} + +void nes_bmc_s700_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_bmc_ball11_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_bmc_ball11_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_reg[0] = 1; + m_reg[1] = 0; + set_banks(); +} + +void nes_bmc_22games_device::device_start() +{ + common_start(); +} + +void nes_bmc_22games_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(7); + chr8(0, m_chr_source); +} + +void nes_bmc_64y2k_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_bmc_64y2k_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + chr8(0, m_chr_source); + + m_reg[0] = 0x80; + m_reg[1] = 0x43; + m_reg[2] = m_reg[3] = 0; + set_prg(); + set_nt_mirroring(PPU_MIRROR_VERT); +} + +void nes_bmc_12in1_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_bmc_12in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + m_reg[0] = 0; + m_reg[1] = 0; + m_reg[2] = 0; + update_banks(); +} + +void nes_bmc_20in1_device::device_start() +{ + common_start(); +} + +void nes_bmc_20in1_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); + set_nt_mirroring(PPU_MIRROR_VERT); +} + +void nes_bmc_21in1_device::device_start() +{ + common_start(); +} + +void nes_bmc_21in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_bmc_31in1_device::device_start() +{ + common_start(); +} + +void nes_bmc_31in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(1); + chr8(0, m_chr_source); +} + +void nes_bmc_35in1_device::device_start() +{ + common_start(); +} + +void nes_bmc_35in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0); + chr8(0, m_chr_source); +} + +void nes_bmc_36in1_device::device_start() +{ + common_start(); +} + +void nes_bmc_36in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(m_prg_chunks - 1); + prg16_cdef(m_prg_chunks - 1); + chr8(0, m_chr_source); +} + +void nes_bmc_64in1_device::device_start() +{ + common_start(); +} + +void nes_bmc_64in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0); + chr8(0, m_chr_source); +} + +void nes_bmc_70in1_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); + save_item(NAME(m_mode)); +} + +void nes_bmc_70in1_device::pcb_reset() +{ + prg16_89ab(m_prg_chunks - 1); + prg16_cdef(m_prg_chunks - 1); + chr8(0, CHRROM); + m_reg[0] = 0; + m_reg[1] = 0; + m_mode = 0; +} + +void nes_bmc_72in1_device::device_start() +{ + common_start(); +} + +void nes_bmc_72in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_bmc_76in1_device::device_start() +{ + common_start(); + save_item(NAME(m_latch1)); + save_item(NAME(m_latch2)); +} + +void nes_bmc_76in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_latch1 = 0; + m_latch2 = 0; +} + +void nes_bmc_110in1_device::device_start() +{ + common_start(); +} + +void nes_bmc_110in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(1); + chr8(0, m_chr_source); + set_nt_mirroring(PPU_MIRROR_VERT); +} + +void nes_bmc_150in1_device::device_start() +{ + common_start(); +} + +void nes_bmc_150in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0); + chr8(0, m_chr_source); +} + +void nes_bmc_190in1_device::device_start() +{ + common_start(); +} + +void nes_bmc_190in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0); + chr8(0, m_chr_source); +} + +void nes_bmc_800in1_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); + save_item(NAME(m_mode)); +} + +void nes_bmc_800in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(m_prg_chunks - 1); + prg16_cdef(m_prg_chunks - 1); + chr8(0, m_chr_source); + m_reg[0] = 0; + m_reg[1] = 0; + m_mode = 0; +} + +void nes_bmc_1200in1_device::device_start() +{ + common_start(); + save_item(NAME(m_vram_protect)); +} + +void nes_bmc_1200in1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0); + chr8(0, m_chr_source); + m_vram_protect = 0; +} + +void nes_bmc_gold150_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_bmc_gold150_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_latch = 0; +} + +void nes_bmc_gold260_device::device_start() +{ + common_start(); +} + +void nes_bmc_gold260_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_bmc_ch001_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_bmc_ch001_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_latch = 0; +} + +void nes_bmc_super22_device::device_start() +{ + common_start(); +} + +void nes_bmc_super22_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + + +// This PCB is fully emulated here :) +void nes_bmc_4in1reset_device::device_start() +{ + common_start(); + m_latch = -1; + save_item(NAME(m_latch)); +} + +void nes_bmc_4in1reset_device::pcb_reset() +{ + m_latch++; + m_latch &= 3; + chr8(m_latch, CHRROM); + prg16_89ab(m_latch); + prg16_cdef(m_latch); +} + + +void nes_bmc_42in1reset_device::device_start() +{ + common_start(); + m_latch = -1; + save_item(NAME(m_reg)); +} + +void nes_bmc_42in1reset_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + m_latch++; + m_latch &= 1; + chr8(0, m_chr_source); + prg32(m_latch << 4); + + m_reg[0] = 0; + m_reg[1] = 0; +} + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Active Entertainment Action 52 board emulation + + iNES: mapper 228 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_action52_device::write_h) +{ + UINT8 pmode = offset & 0x20; + int pbank = (offset & 0x1fc0) >> 6; + int cbank = (data & 0x03) | ((offset & 0x0f) << 2); + LOG_MMC(("ae_act52_w, offset: %04x, data: %02x\n", offset, data)); + + set_nt_mirroring(BIT(offset, 13) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + chr8(cbank, CHRROM); + + if (pmode) + { + prg16_89ab(pbank); + prg16_cdef(pbank); + } + else + prg32(pbank >> 1); +} + +/*------------------------------------------------- + + Caltron 6 in 1 Board + + Games: 6 in 1 by Caltron + + iNES: mapper 41 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_caltron_device::write_m) +{ + LOG_MMC(("caltron write_m, offset: %04x, data: %02x\n", offset, data)); + + m_latch = offset & 0xff; + set_nt_mirroring(BIT(data, 5) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + prg32(offset & 0x07); +} + +WRITE8_MEMBER(nes_caltron_device::write_h) +{ + LOG_MMC(("caltron write_h, offset: %04x, data: %02x\n", offset, data)); + + if (m_latch & 0x04) + chr8(((m_latch & 0x18) >> 1) | (data & 0x03), CHRROM); +} + +/*------------------------------------------------- + + Rumblestation Board + + Games: Rumblestation 15 in 1 + + iNES: mapper 46 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_rumblestat_device::write_m) +{ + LOG_MMC(("rumblestation write_m, offset: %04x, data: %02x\n", offset, data)); + + m_prg = (m_prg & 0x01) | ((data & 0x0f) << 1); + m_chr = (m_chr & 0x07) | ((data & 0xf0) >> 1); + prg32(m_prg); + chr8(m_chr, CHRROM); +} + +WRITE8_MEMBER(nes_rumblestat_device::write_h) +{ + LOG_MMC(("rumblestation write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + m_prg = (m_prg & ~0x01) | (data & 0x01); + m_chr = (m_chr & ~0x07) | ((data & 0x70) >> 4); + prg32(m_prg); + chr8(m_chr, CHRROM); +} + +/*------------------------------------------------- + + Supervision 16 in 1 Board + + Games: Supervision 16 in 1 + + iNES: mapper 53 + + In MESS: Partially Supported. (the dump contains 32KB of + EEPROM which is not currently handled well) + + -------------------------------------------------*/ + +void nes_svision16_device::update_prg() +{ + int base = (m_latch1 & 0x0f) << 3; + if (m_latch1 & 0x10) + { + prg16_89ab((base | (m_latch2 & 7)) + 2); // +2 due to the eeprom + prg16_cdef((base | 0x07) + 2); // +2 due to the eeprom + } + else + { + prg16_89ab(0); + prg16_cdef(1); + } + +} + +READ8_MEMBER(nes_svision16_device::read_m) +{ + int bank = (((m_latch1 & 0x0f) << 4) | 0x0f) + 4 ; // +4 due to the eeprom + LOG_MMC(("svision16 read_m, offset: %04x\n", offset)); + return m_prg[((bank * 0x2000) + (offset & 0x1fff)) & m_prg_mask]; +} + +WRITE8_MEMBER(nes_svision16_device::write_m) +{ + LOG_MMC(("svision16 write_m, offset: %04x, data: %02x\n", offset, data)); + + m_latch1 = data; + update_prg(); + set_nt_mirroring(BIT(data, 5) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +WRITE8_MEMBER(nes_svision16_device::write_h) +{ + LOG_MMC(("svision16 write_h, offset: %04x, data: %02x\n", offset, data)); + m_latch2 = data; + update_prg(); +} + +/*------------------------------------------------- + + Bootleg Board N625092 + + Games: 400 in 1, 700 in 1, 1000 in 1 + + iNES: mapper 221 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_n625092_device::set_prg(UINT8 reg1, UINT8 reg2) +{ + UINT8 helper1, helper2; + + helper1 = !(reg1 & 0x01) ? reg2 : (reg1 & 0x80) ? reg2 : (reg2 & 0x06) | 0x00; + helper2 = !(reg1 & 0x01) ? reg2 : (reg1 & 0x80) ? 0x07 : (reg2 & 0x06) | 0x01; + + prg16_89ab(helper1 | ((reg1 & 0x70) >> 1)); + prg16_cdef(helper2 | ((reg1 & 0x70) >> 1)); +} + +WRITE8_MEMBER(nes_n625092_device::write_h) +{ + LOG_MMC(("n625092 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 0x4000) + { + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + offset = (offset >> 1) & 0xff; + + if (m_latch1 != offset) + { + m_latch1 = offset; + set_prg(m_latch1, m_latch2); + } + } + else + { + offset &= 0x07; + + if (m_latch2 != offset) + { + m_latch2 = offset; + set_prg(m_latch1, m_latch2); + } + } +} + + +/*------------------------------------------------- + + Board BMC-A65AS + + Games: 3-in-1 (N068) + + In MESS: Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_a65as_device::write_h) +{ + UINT8 helper = (data & 0x30) >> 1; + LOG_MMC(("a65as write_h, offset: %04x, data: %02x\n", offset, data)); + + if (data & 0x80) + set_nt_mirroring(BIT(data, 5) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW); + else + set_nt_mirroring(BIT(data, 3) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + if (data & 0x40) + prg32(data >> 1); + else + { + prg16_89ab(helper | (data & 0x07)); + prg16_cdef(helper | 0x07); + } +} + +/*------------------------------------------------- + + Board BMC-T-262 + + Games: 4-in-1 (D-010), 8-in-1 (A-020) + + In MESS: Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_t262_device::write_h) +{ + UINT8 mmc_helper; + LOG_MMC(("t262 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (m_latch2 || offset == 0) + { + m_latch1 = (m_latch1 & 0x38) | (data & 0x07); + prg16_89ab(m_latch1); + } + else + { + m_latch2 = 1; + set_nt_mirroring(BIT(data, 1) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + mmc_helper = ((offset >> 3) & 0x20) | ((offset >> 2) & 0x18); + m_latch1 = mmc_helper | (m_latch1 & 0x07); + prg16_89ab(m_latch1); + prg16_cdef(mmc_helper | 0x07); + } +} + + +/*------------------------------------------------- + + BMC-NOVELDIAMOND and BMC-999999in1 + + Unknown Bootleg Multigame Board + Games: I only found 'Novel Diamond 999999-in-1.unf' using + this mapper (hence the code is used for BMC_NOVELDIAMOND + board). The code is included here in case a mapper 54 + dump arises. + + iNES: mappers 54 and 213 + + In MESS: Partial Support. + + -------------------------------------------------*/ + +// Are this correct or should they work the same? +WRITE8_MEMBER(nes_novel1_device::write_h) +{ + LOG_MMC(("novel1 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(offset & 0x03); + chr8(offset & 0x07, CHRROM); +} + +WRITE8_MEMBER(nes_novel2_device::write_h) +{ + LOG_MMC(("novel2 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(offset >> 1); + chr8(offset >> 3, CHRROM); +} + + +/*------------------------------------------------- + + Board UNL-STUDYNGAME + + Games: Study n Game 32 in 1 + + iNES: mapper 39 + + In MESS: Partially Supported (problems with PRG bankswitch, + only keyboard exercise work). + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_studyngame_device::write_h) +{ + LOG_MMC(("studyngame write_h, offset: %04x, data: %02x\n", offset, data)); + prg32(data); +} + +/*------------------------------------------------- + + BMC-SUPERGUN-20IN1 + + Unknown Bootleg Multigame Board + Games: Super Gun 20 in 1 + + iNES: mapper 214 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sgun20in1_device::write_h) +{ + LOG_MMC(("supergun20in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg16_89ab(offset >> 2); + prg16_cdef(offset >> 2); + chr8(offset, CHRROM); +} + +/*------------------------------------------------- + + BMC-VT5201 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_vt5201_device::write_h) +{ + LOG_MMC(("vt5201 wirte_h, offset: %04x, data: %02x\n", offset, data)); + + m_latch = BIT(offset, 8); + + // not sure about this mirroring bit!! + // without it TN 95 in 1 has glitches in Lunar Ball; with it TN 95 in 1 has glitches in Galaxian! + set_nt_mirroring(BIT(data, 3) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + if (BIT(offset, 7)) + { + prg16_89ab((offset >> 4) & 0x07); + prg16_cdef((offset >> 4) & 0x07); + } + else + prg32((offset >> 5) & 0x03); + chr8(offset, CHRROM); +} + +READ8_MEMBER(nes_vt5201_device::read_h) +{ + LOG_MMC(("bmc_vt5201 read_h, offset: %04x\n", offset)); + // m_dipsetting = ioport("CARTDIPS")->read(); + + if (m_latch) + return m_dipsetting; // cart mode, depending on the Dip Switches (always zero atm, given we have no way to add cart-based DIPs) + else + return hi_access_rom(offset); +} + +/*------------------------------------------------- + + BMC-810544-C-A1 + + Games: 200-in-1 Elfland + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_810544c_device::write_h) +{ + UINT8 bank = (offset >> 7); + LOG_MMC(("810544 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (!BIT(offset, 6)) + { + prg16_89ab((bank << 1) | BIT(offset, 5)); + prg16_cdef((bank << 1) | BIT(offset, 5)); + } + else + prg32(bank); + + set_nt_mirroring(BIT(offset, 4) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + chr8(offset & 0x0f, CHRROM); +} + +/*------------------------------------------------- + + BMC-NTD-03 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_ntd03_device::write_h) +{ + UINT8 pbank = (offset >> 10) & 0x1e; + UINT8 cbank = ((offset & 0x300) >> 5) | (offset & 0x07); + LOG_MMC(("ntd03 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (BIT(offset, 7)) + { + prg16_89ab(pbank | BIT(offset, 6)); + prg16_cdef(pbank | BIT(offset, 6)); + } + else + prg32(pbank >> 1); + + set_nt_mirroring(BIT(offset, 10) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + chr8(cbank, CHRROM); +} + +/*------------------------------------------------- + + BMC-GHOSTBUSTERS63IN1 + + in MESS: only preliminar support + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_gb63_device::chr_w) +{ + int bank = offset >> 10; + + if (!m_vram_disable) + m_chr_access[bank][offset & 0x3ff] = data; +} + +void nes_bmc_gb63_device::update_banks() +{ + set_nt_mirroring(BIT(m_reg[0], 6) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ); + + if (BIT(m_reg[0], 5)) + { + prg16_89ab(m_reg[0] & 0x1f); + prg16_cdef(m_reg[0] & 0x1f); + } + else + prg32((m_reg[0] & 0x1f) >> 1); + +// according to FCEUMM source, the game should be able to disable the VRAM, but this stops the game from working +// maybe the VRAM disable does not work at start? +// m_vram_disable = BIT(m_reg[1], 1) ? 0 : 1; +} + +WRITE8_MEMBER(nes_bmc_gb63_device::write_h) +{ + LOG_MMC(("bmc_gb63 write_h, offset: %04x, data: %02x\n", offset, data)); + + m_reg[offset & 1] = data; + m_latch = BIT(m_reg[0], 7) | (BIT(m_reg[1], 0) << 1); + update_banks(); +} + +READ8_MEMBER(nes_bmc_gb63_device::read_h) +{ + LOG_MMC(("bmc_gb63 read_h, offset: %04x\n", offset)); + // m_dipsetting = ioport("CARTDIPS")->read(); + + if (m_latch == 1) + return m_open_bus; // open bus + else + return hi_access_rom(offset); +} + + +/*------------------------------------------------- + + Board BMC-GKA + + Unknown Bootleg Multigame Board + Games: 6 in 1, 54 in 1, 106 in 1 + + iNES: mapper 57 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_gka_device::write_h) +{ + LOG_MMC(("bmc_gka write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset & 0x0800) + m_latch2 = data; + else + m_latch1 = data; + + if (m_latch2 & 0x80) + prg32(2 | (m_latch2 >> 6)); + else + { + prg16_89ab((m_latch2 >> 5) & 0x03); + prg16_cdef((m_latch2 >> 5) & 0x03); + } + + set_nt_mirroring((m_latch2 & 0x08) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + chr8((m_latch1 & 0x03) | (m_latch2 & 0x07) | ((m_latch2 & 0x10) >> 1), CHRROM); +} + + +/*------------------------------------------------- + + Board BMC-GKB + + Unknown Bootleg Multigame Board + Games: 68 in 1, 73 in 1, 98 in 1 + + iNES: mapper 58 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_gkb_device::write_h) +{ + UINT8 bank = (offset & 0x40) ? 0 : 1; + LOG_MMC(("bmc_gkb write_h, offset: %04x, data: %02x\n", offset, data)); + + prg16_89ab(offset & ~bank); + prg16_cdef(offset | bank); + chr8(offset >> 3, m_chr_source); + set_nt_mirroring(BIT(data, 7) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +/*------------------------------------------------- + + Board BMC-WS + + Games: Super 40-in-1 + + In MESS: Partially Supported (some games, like Galaxian, have + issues) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_ws_device::write_m) +{ + UINT8 mmc_helper; + LOG_MMC(("bmc_ws write_m, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 0x1000) + { + switch (offset & 0x01) + { + case 0: + if (!m_latch) + { + m_latch = data & 0x20; + set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + mmc_helper = (~data & 0x08) >> 3; + prg16_89ab(data & ~mmc_helper); + prg16_cdef(data | mmc_helper); + } + break; + case 1: + if (!m_latch) + chr8(data, CHRROM); + break; + } + } +} + +/*------------------------------------------------- + + Board BMC-11160 (by TXC?) + + Games: 6 in 1 (MGC-023) + + In MESS: Partially Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_11160_device::write_h) +{ + LOG_MMC(("bmc_11160 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32((data & 0x70) >> 4); + chr8(((data & 0x70) >> 2) | (data & 3), m_chr_source); + set_nt_mirroring(BIT(data, 7) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +/*------------------------------------------------- + + Board BMC-G-146 + + Games: 1994 Super HIK 14 in 1 (G-136) + + In MESS: Partially Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_g146_device::write_h) +{ + LOG_MMC(("bmc_g146 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset & 0x800) + { + // UNROM mode + int helper = offset & BIT(offset, 6); + prg16_89ab((offset & 0x1f) | helper); + prg16_cdef((offset & 0x18) | 7); + } + else if (offset & 0x40) + { + // 16KB mode + prg16_89ab(offset & 0x1f); + prg16_cdef(offset & 0x1f); + } + else + prg32((offset & 0x1f) >> 4); + + set_nt_mirroring(!BIT(offset, 7) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +/*------------------------------------------------- + + Board BMC-8157 + + Games: 4 in 1 1993 (CK-001) + + In MESS: Partially Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_8157_device::write_h) +{ + LOG_MMC(("bmc_8157 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg16_89ab((offset >> 2) & 0x1f); + if (offset & 0x200) + prg16_cdef(offset & 0x1f); + else + prg16_cdef((offset & 0x18) | 7); + + set_nt_mirroring(!BIT(offset, 1) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +READ8_MEMBER(nes_bmc_8157_device::read_h) +{ + LOG_MMC(("bmc_8157 read_h, offset: %04x\n", offset)); + // m_dipsetting = ioport("CARTDIPS")->read(); + +// UINT8 val = hi_access_rom(offset); +// return val | m_noise; // the first write_h sets m_noise=0xff or 0 depending on dsw + + return hi_access_rom(offset); +} + + +/*------------------------------------------------- + + BMC-SUPERHIK_300IN1 + + Unknown Bootleg Multigame Board + Games: 100000 in 1, Super HIK 300 in 1, 1997 in 1 + + iNES: mapper 212 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_hik300_device::write_h) +{ + LOG_MMC(("bmc_hik300 write_h, offset: %04x, data: %02x\n", offset, data)); + + set_nt_mirroring(BIT(data, 3) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + chr8(offset, CHRROM); + + if (offset < 0x4000) + { + prg16_89ab(offset); + prg16_cdef(offset); + } + else + prg32(offset >> 1); +} + +/*------------------------------------------------- + + BMC-SUPER-700IN1 + + Unknown Bootleg Multigame Board + Games: Super 700 in 1 + + iNES: mapper 62 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_s700_device::write_h) +{ + LOG_MMC(("bmc_s700 write_h, offset :%04x, data: %02x\n", offset, data)); + + chr8(((offset & 0x1f) << 2) | (data & 0x03), CHRROM); + + if (offset & 0x20) + { + prg16_89ab((offset & 0x40) | ((offset >> 8) & 0x3f)); + prg16_cdef((offset & 0x40) | ((offset >> 8) & 0x3f)); + } + else + { + prg32(((offset & 0x40) | ((offset >> 8) & 0x3f)) >> 1); + } + + set_nt_mirroring(BIT(data, 7) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +/*------------------------------------------------- + + BMC-BALLGAMES-11IN1 + + Known Boards: Unknown Multigame Bootleg Board + Games: 11 in 1 Ball Games + + iNES: mapper 51 + + In MESS: Partially Supported. + + -------------------------------------------------*/ + +void nes_bmc_ball11_device::set_banks() +{ + set_nt_mirroring((m_reg[0] == 3) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + if (m_reg[0] & 0x01) + { + prg32(m_reg[1]); + } + else + { + prg16_89ab((m_reg[1] << 1) | (m_reg[0] >> 1)); + prg16_cdef((m_reg[1] << 1) | 0x07); + } +} + +WRITE8_MEMBER(nes_bmc_ball11_device::write_m) +{ + LOG_MMC(("bmc_ball11 write_m, offset: %04x, data: %02x\n", offset, data)); + + m_reg[0] = ((data >> 1) & 0x01) | ((data >> 3) & 0x02); + set_banks(); +} + +WRITE8_MEMBER(nes_bmc_ball11_device::write_h) +{ + LOG_MMC(("bmc_ball11 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6000) + { + case 0x4000: // here we also update reg[0] upper bit + m_reg[0] = (m_reg[0] & 0x01) | ((data >> 3) & 0x02); + case 0x0000: + case 0x2000: + case 0x6000: + m_reg[1] = data & 0x0f; + set_banks(); + break; + } +} + +/*------------------------------------------------- + + BMC-22GAMES + + Unknown Bootleg Multigame Board + Games: 22 in 1 + + iNES: mapper 230 + + In MESS: Partially Supported. It would need a reset + to work (not possible yet) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_22games_device::write_h) +{ + LOG_MMC(("bmc_22games write_h, offset: %04x, data: %02x\n", offset, data)); + + if (1) // this should flip at reset + { + prg16_89ab(data & 0x07); + } + else + { + if (data & 0x20) + { + prg16_89ab((data & 0x1f) + 8); + prg16_cdef((data & 0x1f) + 8); + } + else + { + prg16_89ab((data & 0x1f) + 8); + prg16_cdef((data & 0x1f) + 9); + } + set_nt_mirroring(BIT(data, 6) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ); + } +} + +/*------------------------------------------------- + + Board BMC-64IN1NOREPEAT + + Games: 64-in-1 Y2K + + In MESS: Supported + + -------------------------------------------------*/ + +void nes_bmc_64y2k_device::set_prg() +{ + UINT8 helper1 = (m_reg[1] & 0x1f); + UINT8 helper2 = (helper1 << 1) | ((m_reg[1] & 0x40) >> 6); + + if (m_reg[0] & 0x80) + { + if (m_reg[1] & 0x80) + prg32(helper1); + else + { + prg16_89ab(helper2); + prg16_cdef(helper2); + } + } + else + prg16_cdef(helper2); +} + +WRITE8_MEMBER(nes_bmc_64y2k_device::write_l) +{ + LOG_MMC(("bmc64y2k write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + switch (offset) + { + case 0x1000: + case 0x1001: + case 0x1002: + case 0x1003: + m_reg[offset & 0x03] = data; + set_prg(); + chr8(((m_reg[0] >> 1) & 0x03) | (m_reg[2] << 2), CHRROM); + break; + } + if (offset == 0x1000) /* write to reg[0] also sets mirroring */ + set_nt_mirroring(BIT(data, 5) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +WRITE8_MEMBER(nes_bmc_64y2k_device::write_h) +{ + LOG_MMC(("bmc64y2k write_h, offset: %04x, data: %02x\n", offset, data)); + + m_reg[3] = data; // reg[3] is currently unused?!? +} + +/*------------------------------------------------- + + BMC-12IN1 + + Unknown Bootleg Multigame Board + Games: + + iNES: + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_bmc_12in1_device::update_banks() +{ + int bank = (m_reg[2] & 3) << 3; + + chr4_0((m_reg[0] >> 3) | (bank << 2), m_chr_source); + chr4_4((m_reg[1] >> 3) | (bank << 2), m_chr_source); + + if (m_reg[2] & 8) + prg32(((m_reg[0] & 7) >> 1) | bank); + else + { + prg16_89ab((m_reg[0] & 7) | bank); + prg16_cdef(7 | bank); + } + + set_nt_mirroring(BIT(m_reg[2], 2) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +WRITE8_MEMBER(nes_bmc_12in1_device::write_h) +{ + LOG_MMC(("bmc_12in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6000) + { + case 0x2000: m_reg[0] = data; break; + case 0x4000: m_reg[1] = data; break; + case 0x6000: m_reg[2] = data; break; + } + update_banks(); +} + +/*------------------------------------------------- + + BMC-20IN1 + + Unknown Bootleg Multigame Board + Games: 20 in 1 + + iNES: mapper 231 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_20in1_device::write_h) +{ + LOG_MMC(("bmc_20in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + set_nt_mirroring(BIT(data, 7) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + prg16_89ab((offset & 0x1e)); + prg16_cdef((offset & 0x1e) | ((offset & 0x20) ? 1 : 0)); +} + +/*------------------------------------------------- + + BMC-21IN1 + + Unknown Bootleg Multigame Board + Games: 8 in 1, 21 in 1 + + iNES: mapper 201 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_21in1_device::write_h) +{ + LOG_MMC(("bmc_21in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(offset & 0x03); + chr8(offset & 0x03, CHRROM); +} + +/*------------------------------------------------- + + BMC-31IN1 + + Unknown Bootleg Multigame Board + Games: 31 in 1 + + iNES: mapper 229 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_31in1_device::write_h) +{ + LOG_MMC(("bmc_31in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + set_nt_mirroring(BIT(data, 5) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + chr8(offset, CHRROM); + + if ((offset & 0x1e) == 0) + { + prg16_89ab(0); + prg16_89ab(1); + } + else + { + prg16_89ab(offset & 0x1f); + prg16_89ab(offset & 0x1f); + } +} + +/*------------------------------------------------- + + BMC-35IN1 + + Unknown Bootleg Multigame Board + Games: 35 in 1 + + iNES: mapper 203 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_35in1_device::write_h) +{ + LOG_MMC(("bmc_35in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg16_89ab((data >> 2) & 0x03); + prg16_cdef((data >> 2) & 0x03); + chr8(data & 0x03, CHRROM); +} + +/*------------------------------------------------- + + BMC-36IN1 + + Unknown Bootleg Multigame Board + Games: 36 in 1, 1200 in 1 + + iNES: mapper 200 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_36in1_device::write_h) +{ + LOG_MMC(("bmc_36in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg16_89ab(offset & 0x07); + prg16_cdef(offset & 0x07); + chr8(offset & 0x07, CHRROM); + + set_nt_mirroring(BIT(data, 3) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +/*------------------------------------------------- + + BMC-64IN1 + + Unknown Bootleg Multigame Board + Games: 64 in 1 + + iNES: mapper 204 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_64in1_device::write_h) +{ + int bank = (offset >> 1) & (offset >> 2) & 0x01; + + LOG_MMC(("bmc_64in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg16_89ab(offset & ~bank); + prg16_cdef(offset | bank); + chr8(offset & ~bank, CHRROM); + + set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HORZ: PPU_MIRROR_VERT); +} + +/*------------------------------------------------- + + BMC-70IN1 + + Unknown Bootleg Multigame Board + Games: + + iNES: mapper + + This is same hardware as BMC-800IN1 below, but this + cart has CHR and slightly diff bankswitch. + DSW not emulated yet. + + In MESS: Preliminary Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_70in1_device::write_h) +{ + LOG_MMC(("bmc70in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 0x4000) + { + set_nt_mirroring(BIT(offset, 5) ? PPU_MIRROR_HORZ: PPU_MIRROR_VERT); + chr8(offset & 7, CHRROM); + } + else + { + switch (offset & 0x30) + { + case 0x00: m_mode = 0x0; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x7); m_reg[1] = m_reg[0] | 0x7; break; + case 0x10: m_mode = 0x1; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x7); m_reg[1] = m_reg[0] | 0x7; break; + case 0x20: m_mode = 0x0; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x6); m_reg[1] = m_reg[0] | 0x1; break; + case 0x30: m_mode = 0x0; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x7); m_reg[1] = m_reg[0] | 0x0; break; + } + prg16_89ab(m_reg[0]); + prg16_cdef(m_reg[1]); + } +} + +READ8_MEMBER(nes_bmc_70in1_device::read_h) +{ + LOG_MMC(("bmc70in1 read_h, offset: %04x\n", offset)); + + if (m_mode) + offset = (offset & 0x7ff0) | m_mode; + + return hi_access_rom(offset); +} + +/*------------------------------------------------- + + BMC-72IN1 + + Unknown Bootleg Multigame Board + Games: 72 in 1, 115 in 1 and other multigame carts + + iNES: mapper 225 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_72in1_device::write_h) +{ + int hi_bank = offset & 0x40; + int size_16 = offset & 0x1000; + int bank = (offset & 0xf80) >> 7; + + LOG_MMC(("bmc_72in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + chr8(offset, CHRROM); + set_nt_mirroring((offset & 0x2000) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + if (size_16) + { + bank <<= 1; + if (hi_bank) + bank ++; + + prg16_89ab(bank); + prg16_cdef(bank); + } + else + prg32(bank); +} + +/*------------------------------------------------- + + BMC-76IN1 + + Unknown Bootleg Multigame Board + Games: 76 in 1, Super 42 in 1 + + iNES: mapper 226 + + In MESS: Supported. + + -------------------------------------------------*/ + +// does this work for super42in1 as well?!? +WRITE8_MEMBER(nes_bmc_76in1_device::write_h) +{ + int hi_bank; + int size_16; + int bank; + + LOG_MMC(("bmc_76in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset & 0x01) + m_latch2 = data; + else + m_latch1 = data; + + set_nt_mirroring(BIT(m_latch1, 6) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + hi_bank = m_latch1 & 0x01; + size_16 = m_latch1 & 0x20; + bank = ((m_latch1 & 0x1e) >> 1) | ((m_latch1 & 0x80) >> 3) | ((m_latch2 & 0x01) << 5); + + if (size_16) + { + bank <<= 1; + if (hi_bank) + bank ++; + + prg16_89ab(bank); + prg16_cdef(bank); + } + else + prg32(bank); +} + +/*------------------------------------------------- + + BMC-110IN1 + + Known Boards: Unknown Bootleg Board + Games: 110 in 1 + + iNES: mapper 255 + + In MESS: Preliminary support. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_110in1_device::write_h) +{ + UINT8 helper1 = (offset >> 12) ? 0 : 1; + UINT8 helper2 = ((offset >> 8) & 0x40) | ((offset >> 6) & 0x3f); + + LOG_MMC(("bmc_110in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + set_nt_mirroring((offset & 0x2000) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + prg16_89ab(helper1 & ~helper2); + prg16_cdef(helper1 | helper2); + chr8(((offset >> 8) & 0x40) | (offset & 0x3f), CHRROM); +} + + +/*------------------------------------------------- + + BMC-150IN1 + + Unknown Bootleg Multigame Board + Games: 150 in 1 + + iNES: mapper 202 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_150in1_device::write_h) +{ + int bank = (offset >> 1) & 0x07; + + LOG_MMC(("bmc_150in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg16_89ab(bank); + prg16_cdef(bank + (((bank & 0x06) == 0x06) ? 1 : 0)); + chr8(bank, CHRROM); + + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ: PPU_MIRROR_VERT); +} + + +/*------------------------------------------------- + + Board BMC-190IN1 + + Games: 190-in-1 + + In MESS: Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_190in1_device::write_h) +{ + LOG_MMC(("bmc190in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + offset >>= 2; + prg16_89ab(offset); + prg16_cdef(offset); + chr8(offset, CHRROM); +} + +/*------------------------------------------------- + + BMC-800IN1 + + Unknown Bootleg Multigame Board + Games: + + iNES: mapper + + DSW not emulated yet. + + In MESS: Preliminary Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_800in1_device::write_h) +{ + LOG_MMC(("bmc800in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 0x4000) + { + set_nt_mirroring(BIT(offset, 5) ? PPU_MIRROR_HORZ: PPU_MIRROR_VERT); + + m_reg[0] = (m_reg[0]) | ((offset << 3) & 0x38); + m_reg[1] = (m_reg[1]) | ((offset << 3) & 0x38); + } + else + { + switch (offset & 0x30) + { + case 0x00: m_mode = 0x0; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x7); m_reg[1] = m_reg[0] | 0x7; break; + case 0x10: m_mode = 0x1; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x7); m_reg[1] = m_reg[0] | 0x7; break; + case 0x20: m_mode = 0x0; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x6); m_reg[1] = m_reg[0] | 0x1; break; + case 0x30: m_mode = 0x0; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x7); m_reg[1] = m_reg[0] | 0x0; break; + } + } + prg16_89ab(m_reg[0]); + prg16_cdef(m_reg[1]); +} + +READ8_MEMBER(nes_bmc_800in1_device::read_h) +{ + LOG_MMC(("bmc800in1 read_h, offset: %04x\n", offset)); + + if (m_mode) + offset = (offset & 0x7ff0) | m_mode; + + return hi_access_rom(offset); +} + +/*------------------------------------------------- + + BMC-1200IN1 + + Unknown Bootleg Multigame Board + Games: 1200 in 1, 295 in 1, 76 in 1 + + iNES: mapper 227 + + In MESS: Preliminary Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_1200in1_device::chr_w) +{ + int bank = offset >> 10; + + if (!m_vram_protect) + m_chr_access[bank][offset & 0x3ff] = data; +} + + +WRITE8_MEMBER(nes_bmc_1200in1_device::write_h) +{ + int bank = ((offset >> 2) & 0x1f) | ((offset & 0x0100) >> 3); + + LOG_MMC(("bmc_1200in1 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset & 0x80) + { +// m_vram_protect = 1; + prg16_89ab(bank); + prg16_cdef(bank + (offset & 1)); + } + else + { + int low_mask = (offset & 1) ? 0x3e : 0xff; + +// m_vram_protect = 0; + if (!BIT(offset, 9)) + { + prg16_89ab(bank & low_mask); + prg16_cdef(bank & 0x38); + } + else + { + prg16_89ab(bank & low_mask); + prg16_cdef(bank | 0x07); + } + } + + set_nt_mirroring(BIT(offset, 1) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + + +/*------------------------------------------------- + + BMC-GOLDEN260IN1 + + Unknown Bootleg Multigame Board + Games: + + iNES: mapper 235 + + In MESS: Preliminary Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_gold260_device::write_h) +{ + int bank = (offset & 0x1f) | ((offset & 0x0300) >> 3); + LOG_MMC(("bmc_gold260 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset & 0x400) + set_nt_mirroring(PPU_MIRROR_LOW); + else + set_nt_mirroring(BIT(offset, 13) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + if (offset & 0x800) + { + bank = (bank << 1) | BIT(offset, 12); + prg16_89ab(bank); + prg16_cdef(bank); + } + else + prg32(bank); +} + + +/*------------------------------------------------- + + BMC-GOLDEN150IN1 + + Unknown Bootleg Multigame Board + Games: + + iNES: mapper 235 + + Same as the above + open bus in 0x8000-0xffff when + enabled + + In MESS: Preliminary Supported. + + -------------------------------------------------*/ + + +WRITE8_MEMBER(nes_bmc_gold150_device::write_h) +{ + int bank = (offset & 0x1f) | ((offset & 0x0200) >> 4); + LOG_MMC(("bmc_gold150 write_h, offset: %04x, data: %02x\n", offset, data)); + + m_latch = (offset & 0x0100); + + if (offset & 0x400) + set_nt_mirroring(PPU_MIRROR_LOW); + else + set_nt_mirroring(BIT(offset, 13) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + if (offset & 0x800) + { + bank = (bank << 1) | BIT(offset, 12); + prg16_89ab(bank); + prg16_cdef(bank); + } + else + prg32(bank); +} + +READ8_MEMBER(nes_bmc_gold150_device::read_h) +{ + LOG_MMC(("bmc_gold150 read_h, offset: %04x\n", offset)); + + if (m_latch) // open bus + return m_open_bus; + else + return hi_access_rom(offset); +} + + +/*------------------------------------------------- + + BMC-POWERFUL-255 + + Unknown Bootleg Multigame Board + Games: + + iNES: mapper 63 + + + In MESS: Preliminary Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_ch001_device::write_h) +{ + int bank = ((offset >> 1) & 0x1fc); + LOG_MMC(("bmc_ch001 write_h, offset: %04x, data: %02x\n", offset, data)); + + m_latch = ((offset & 0x300) == 0x300); + set_nt_mirroring(BIT(offset, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + if (offset & 2) + { + prg8_89(bank + 0); + prg8_ab(bank + 1); + prg8_cd(bank + 2); + prg8_ef(bank + 3); + } + else + { + bank |= (offset & 4) >> 1; + prg8_89(bank + 0); + prg8_ab(bank + 1); + prg8_cd(bank + 0); + prg8_ef(bank + 1); + } + + if (offset & 0x800) // in this case, the last 8KB bank is switched differently... + prg8_ef((offset & 0x07c ) | ((offset & 0x6) ? 0x3 : 0x1)); +} + +READ8_MEMBER(nes_bmc_ch001_device::read_h) +{ + LOG_MMC(("bmc_ch001 read_h, offset: %04x\n", offset)); + + if (m_latch && offset < 0x4000) // open bus + return m_open_bus; + else + return hi_access_rom(offset); +} + +/*------------------------------------------------- + + BMC-SUPER22GAMES + + Unknown Bootleg Multigame Board + Games: + + iNES: + + is there a dsw to access the higher banks above 0x20? + + In MESS: Preliminary Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_super22_device::write_h) +{ + LOG_MMC(("bmc_super22 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (data & 0x20) + { + prg16_89ab(data & 0x1f); + prg16_cdef(data & 0x1f); + } + else + prg32((data & 0x1f) >> 1); + + switch (data & 0xc0) + { + case 0x00: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 0x40: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 0x80: set_nt_mirroring(PPU_MIRROR_HORZ); break; + case 0xc0: set_nt_mirroring(PPU_MIRROR_HIGH); break; + } +} + + +/*------------------------------------------------- + + BMC-RESETBASED4IN1 + + Unknown Bootleg Multigame Board + + Games: + + iNES: + + No need to use handlers. At reset the banks change + and so does the game. + + In MESS: Preliminary Supported. + + -------------------------------------------------*/ + +/*------------------------------------------------- + + BMC-42IN1RESETBASED + + Unknown Bootleg Multigame Board + + Games: + + iNES: + + In MESS: Preliminary Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bmc_42in1reset_device::write_h) +{ + int bank; + LOG_MMC(("bmc_42in1reset write_h, offset: %04x, data: %02x\n", offset, data)); + + m_reg[offset & 1] = data; + bank = (m_reg[0] & 0x1f) | (m_latch << 5) | ((m_reg[1] & 1) << 6); + + if (!(m_reg[0] & 0x20)) + prg32(bank >> 1); + else + { + prg16_89ab(bank); + prg16_cdef(bank); + } + + set_nt_mirroring(BIT(offset, 6) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + +} diff --git a/src/devices/bus/nes/multigame.h b/src/devices/bus/nes/multigame.h new file mode 100644 index 00000000000..dbc824dfd54 --- /dev/null +++ b/src/devices/bus/nes/multigame.h @@ -0,0 +1,894 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_MULTIGAME_H +#define __NES_MULTIGAME_H + +#include "nxrom.h" + + +// ======================> nes_action52_device + +class nes_action52_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_action52_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_caltron_device + +class nes_caltron_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_caltron_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_rumblestat_device + +class nes_rumblestat_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_rumblestat_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_prg, m_chr; +}; + + +// ======================> nes_svision16_device + +class nes_svision16_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_svision16_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + void update_prg(); + UINT8 m_latch1, m_latch2; +}; + + +// ======================> nes_n625092_device + +class nes_n625092_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_n625092_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + void set_prg(UINT8 reg1, UINT8 reg2); + UINT8 m_latch1, m_latch2; +}; + + +// ======================> nes_a65as_device + +class nes_a65as_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_a65as_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_t262_device + +class nes_t262_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_t262_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch1, m_latch2; +}; + + +// ======================> nes_novel1_device + +class nes_novel1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_novel1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_novel2_device + +class nes_novel2_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_novel2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_studyngame_device + +class nes_studyngame_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_studyngame_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_sgun20in1_device + +class nes_sgun20in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sgun20in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_vt5201_device + +class nes_vt5201_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_vt5201_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch, m_dipsetting; +}; + + +// ======================> nes_810544c_device + +class nes_810544c_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_810544c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_ntd03_device + +class nes_ntd03_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ntd03_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_gb63_device + +class nes_bmc_gb63_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_gb63_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual DECLARE_WRITE8_MEMBER(chr_w); + + virtual void pcb_reset(); + +private: + void update_banks(); + UINT8 m_latch, m_dipsetting; + UINT8 m_reg[2]; + int m_vram_disable; +}; + +// ======================> nes_bmc_gka_device + +class nes_bmc_gka_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_gka_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch1, m_latch2; +}; + + +// ======================> nes_bmc_gkb_device + +class nes_bmc_gkb_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_gkb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_ws_device + +class nes_bmc_ws_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_ws_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_bmc_11160_device + +class nes_bmc_11160_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_11160_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_g146_device + +class nes_bmc_g146_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_g146_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_8157_device + +class nes_bmc_8157_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_8157_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_hik300_device + +class nes_bmc_hik300_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_hik300_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_s700_device + +class nes_bmc_s700_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_s700_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_ball11_device + +class nes_bmc_ball11_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_ball11_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + void set_banks(); + UINT8 m_reg[2]; +}; + + +// ======================> nes_bmc_22games_device + +class nes_bmc_22games_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_22games_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_64y2k_device + +class nes_bmc_64y2k_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_64y2k_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + void set_prg(); + UINT8 m_reg[4]; +}; + + +// ======================> nes_bmc_12in1_device + +class nes_bmc_12in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_12in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + void update_banks(); + UINT8 m_reg[3]; +}; + + +// ======================> nes_bmc_20in1_device + +class nes_bmc_20in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_20in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_21in1_device + +class nes_bmc_21in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_21in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_31in1_device + +class nes_bmc_31in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_31in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_35in1_device + +class nes_bmc_35in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_35in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_36in1_device + +class nes_bmc_36in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_36in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_64in1_device + +class nes_bmc_64in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_64in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_70in1_device + +class nes_bmc_70in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_70in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_mode; + UINT8 m_reg[2]; +}; + + +// ======================> nes_bmc_72in1_device + +class nes_bmc_72in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_72in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_76in1_device + +class nes_bmc_76in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_76in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch1, m_latch2; +}; + + +// ======================> nes_bmc_110in1_device + +class nes_bmc_110in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_110in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_150in1_device + +class nes_bmc_150in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_150in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_190in1_device + +class nes_bmc_190in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_190in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_800in1_device + +class nes_bmc_800in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_800in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_mode; + UINT8 m_reg[2]; +}; + + +// ======================> nes_bmc_1200in1_device + +class nes_bmc_1200in1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_1200in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual DECLARE_WRITE8_MEMBER(chr_w); + + virtual void pcb_reset(); + +private: + UINT8 m_vram_protect; +}; + + +// ======================> nes_bmc_gold150_device + +class nes_bmc_gold150_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_gold150_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_bmc_gold260_device + +class nes_bmc_gold260_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_gold260_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_ch001_device + +class nes_bmc_ch001_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_ch001_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_bmc_super22_device + +class nes_bmc_super22_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_super22_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bmc_4in1reset_device + +class nes_bmc_4in1reset_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_4in1reset_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void pcb_reset(); + +private: + int m_latch; +}; + +// ======================> nes_bmc_42in1reset_device + +class nes_bmc_42in1reset_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bmc_42in1reset_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + int m_latch; + UINT8 m_reg[2]; +}; + + + +// device type definition +extern const device_type NES_ACTION52; +extern const device_type NES_CALTRON6IN1; +extern const device_type NES_RUMBLESTATION; +extern const device_type NES_SVISION16; +extern const device_type NES_N625092; +extern const device_type NES_A65AS; +extern const device_type NES_T262; +extern const device_type NES_NOVEL1; +extern const device_type NES_NOVEL2; +extern const device_type NES_STUDYNGAME; +extern const device_type NES_SUPERGUN20IN1; +extern const device_type NES_VT5201; +extern const device_type NES_810544C; +extern const device_type NES_NTD03; +extern const device_type NES_BMC_GB63; +extern const device_type NES_BMC_GKA; +extern const device_type NES_BMC_GKB; +extern const device_type NES_BMC_WS; +extern const device_type NES_BMC_11160; +extern const device_type NES_BMC_G146; +extern const device_type NES_BMC_8157; +extern const device_type NES_BMC_HIK300; +extern const device_type NES_BMC_S700; +extern const device_type NES_BMC_BALL11; +extern const device_type NES_BMC_22GAMES; +extern const device_type NES_BMC_64Y2K; +extern const device_type NES_BMC_12IN1; +extern const device_type NES_BMC_20IN1; +extern const device_type NES_BMC_21IN1; +extern const device_type NES_BMC_31IN1; +extern const device_type NES_BMC_35IN1; +extern const device_type NES_BMC_36IN1; +extern const device_type NES_BMC_64IN1; +extern const device_type NES_BMC_70IN1; +extern const device_type NES_BMC_72IN1; +extern const device_type NES_BMC_76IN1; +extern const device_type NES_BMC_110IN1; +extern const device_type NES_BMC_150IN1; +extern const device_type NES_BMC_190IN1; +extern const device_type NES_BMC_800IN1; +extern const device_type NES_BMC_1200IN1; +extern const device_type NES_BMC_GOLD150; +extern const device_type NES_BMC_GOLD260; +extern const device_type NES_BMC_CH001; +extern const device_type NES_BMC_SUPER22; +extern const device_type NES_BMC_4IN1RESET; +extern const device_type NES_BMC_42IN1RESET; + + +#endif diff --git a/src/devices/bus/nes/namcot.c b/src/devices/bus/nes/namcot.c new file mode 100644 index 00000000000..236effc4a24 --- /dev/null +++ b/src/devices/bus/nes/namcot.c @@ -0,0 +1,688 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Namcot PCBs + + + Here we emulate the following PCBs + + * Namcot 3433 & 3443 (aka DxROM) [mapper 88, 204, 154] + * Namcot 3446 [mapper 76] + * Namcot 3425 [mapper 95] + * Namcot 163 [mapper 19] + * Namcot 175 [mapper 210] + * Namcot 340 [mapper 210] + + TODO: + - add sound feature of Namcot-163 + - Quinty is not working (same issue of Mendel Palace on TxROM boards, of course) + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "namcot.h" +#include "ui/ui.h" + +#include "cpu/m6502/m6502.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_NAMCOT3433 = &device_creator; +const device_type NES_NAMCOT3446 = &device_creator; +const device_type NES_NAMCOT3425 = &device_creator; +const device_type NES_NAMCOT175 = &device_creator; +const device_type NES_NAMCOT340 = &device_creator; +const device_type NES_NAMCOT163 = &device_creator; + + +nes_namcot3433_device::nes_namcot3433_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_namcot3433_device::nes_namcot3433_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NAMCOT3433, "NES Cart Namcot 3433 & 3443 / DxROM PCB", tag, owner, clock, "nes_namcot3433", __FILE__) +{ +} + +nes_namcot3446_device::nes_namcot3446_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NAMCOT3446, "NES Cart Namcot 3446 PCB", tag, owner, clock, "nes_namcot3446", __FILE__) +{ +} + +nes_namcot3425_device::nes_namcot3425_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NAMCOT3425, "NES Cart Namcot 3425 PCB", tag, owner, clock, "nes_namcot3425", __FILE__) +{ +} + +nes_namcot340_device::nes_namcot340_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_namcot340_device::nes_namcot340_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NAMCOT175, "NES Cart Namcot 340 PCB", tag, owner, clock, "nes_namcot340", __FILE__) +{ +} + +nes_namcot175_device::nes_namcot175_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_namcot340_device(mconfig, NES_NAMCOT340, "NES Cart Namcot 175 PCB", tag, owner, clock, "nes_namcot175", __FILE__) +{ +} + +nes_namcot163_device::nes_namcot163_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_namcot340_device(mconfig, NES_NAMCOT163, "NES Cart Namcot 163 PCB", tag, owner, clock, "nes_namcot163", __FILE__) +{ +} + + + +void nes_namcot3433_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_namcot3433_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(m_prg_chunks - 2); + prg16_cdef(m_prg_chunks - 1); + chr8(0, m_chr_source); + + m_latch = 0; +} + +void nes_namcot3446_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_namcot3446_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; +} + +void nes_namcot3425_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + save_item(NAME(m_reg)); +} + +void nes_namcot3425_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_reg, 0, sizeof(m_reg)); +} + +void nes_namcot340_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_n163_ram)); + + m_mapper_sram_size = 0x2000; + m_mapper_sram = m_n163_ram; +} + +void nes_namcot340_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); + set_nt_mirroring(PPU_MIRROR_VERT); + + m_irq_enable = 0; + m_irq_count = 0; +} + +void nes_namcot175_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_wram_protect)); + save_item(NAME(m_n163_ram)); + + m_mapper_sram_size = 0x2000; + m_mapper_sram = m_n163_ram; +} + +void nes_namcot175_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); + set_nt_mirroring(PPU_MIRROR_VERT); + + m_irq_enable = 0; + m_irq_count = 0; + m_wram_protect = 0; +} + +void nes_namcot163_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_wram_protect)); + save_item(NAME(m_latch)); + save_item(NAME(m_chr_bank)); + save_item(NAME(m_n163_ram)); + + m_mapper_sram_size = 0x2000; + m_mapper_sram = m_n163_ram; +} + +void nes_namcot163_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); + set_nt_mirroring(PPU_MIRROR_VERT); + + m_irq_enable = 0; + m_irq_count = 0; + m_wram_protect = 0; + m_latch = 0; + m_chr_bank = 0; +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + DxROM & Namcot 3433 - 3443 - 3453 board emulation + + Games: Dragon Spirit - Aratanaru Densetsu, Namcot Mahjong, Quinty, + Devilman + + These are the same board, but DRROM (and Tengen 800004) have + 4-screen mirroring + + iNES: mappers 88, 206 (same as 88 but possibly 4-screen mirroring), + and 154 (same as 88 but with additional mirroring control) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_namcot3433_device::dxrom_write) +{ + LOG_MMC(("dxrom_write, offset: %04x, data: %02x\n", offset, data)); + + if (!(offset & 1) && m_pcb_ctrl_mirror) + set_nt_mirroring(BIT(data, 6) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW); + + if (offset >= 0x2000) + return; + + switch (offset & 1) + { + case 1: + switch (m_latch & 0x07) + { + case 0: chr2_0(data >> 1, CHRROM); break; + case 1: chr2_2(data >> 1, CHRROM); break; + case 2: chr1_4(data | 0x40, CHRROM); break; + case 3: chr1_5(data | 0x40, CHRROM); break; + case 4: chr1_6(data | 0x40, CHRROM); break; + case 5: chr1_7(data | 0x40, CHRROM); break; + case 6: prg8_89(data); break; + case 7: prg8_ab(data); break; + } + break; + case 0: + m_latch = data; + break; + } +} + + +/*------------------------------------------------- + + Namcot 3446 board emulation + + Games: Digital Devil Monogatari - Megami Tensei + + These are similar Namcot 34x3, but different bankswitch capabilities + + iNES: mapper 76 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_namcot3446_device::write_h) +{ + LOG_MMC(("namcot3446 write_h, offset: %04x, data: %02x\n", offset, data)); + + // NEStopia does not have this! + if (offset >= 0x2000) + { + if (!(offset & 1)) + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + return; + } + + switch (offset & 1) + { + case 1: + switch (m_latch & 0x07) + { + case 2: chr2_0(data, CHRROM); break; + case 3: chr2_2(data, CHRROM); break; + case 4: chr2_4(data, CHRROM); break; + case 5: chr2_6(data, CHRROM); break; + case 6: BIT(m_latch, 6) ? prg8_cd(data) : prg8_89(data); break; + case 7: prg8_ab(data); break; + } + break; + case 0: + m_latch = data; + break; + } +} + +/*------------------------------------------------- + + Namcot 3425 board emulation + + Games: Dragon Buster + + These are similar Namcot 34x3, but with NT mirroring (two + different modes) + + iNES: mapper 95 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_namcot3425_device::write_h) +{ + UINT8 mode; + LOG_MMC(("namcot3425 write_h, offset: %04x, data: %02x\n", offset, data)); + if (offset >= 0x2000) + return; + + switch (offset & 1) + { + case 1: + mode = m_latch & 0x07; + switch (mode) + { + case 0: chr2_0(data >> 1, CHRROM); break; + case 1: chr2_2(data >> 1, CHRROM); break; + case 2: + case 3: + case 4: + case 5: + chr1_x(2 + mode, data, CHRROM); + m_reg[mode - 2] = BIT(data, 5); + if (!BIT(m_latch, 7)) + { + set_nt_page(0, CIRAM, m_reg[0], 1); + set_nt_page(1, CIRAM, m_reg[1], 1); + set_nt_page(2, CIRAM, m_reg[2], 1); + set_nt_page(3, CIRAM, m_reg[3], 1); + } + else + set_nt_mirroring(PPU_MIRROR_HORZ); + break; + case 6: prg8_89(data); break; + case 7: prg8_ab(data); break; + } + break; + case 0: + m_latch = data; + break; + } +} + +/*------------------------------------------------- + + Namcot-340 board emulation + + Games: Famista '92, '93 & '94, Top Striker, + Wagyan Land 2 & 3 + + This (and Namcot-175 below) is a cut-down version + of the Namcot-163 chip, without the sound capabilities. + They also cannot use NTRAM as VRAM and differ for + the mirroring handling + + iNES: mapper 210 + + In MESS: Supported + + -------------------------------------------------*/ + +void nes_namcot340_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (m_irq_count == 0x7fff) // counter does not wrap to 0! + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + else + m_irq_count++; + } + } +} + +WRITE8_MEMBER(nes_namcot340_device::n340_lowrite) +{ + LOG_MMC(("n340_lowrite, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + switch (offset & 0x1800) + { + case 0x1000: /* low byte of IRQ */ + m_irq_count = (m_irq_count & 0x7f00) | data; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x1800: /* high byte of IRQ, IRQ enable in high bit */ + m_irq_count = (m_irq_count & 0xff) | ((data & 0x7f) << 8); + m_irq_enable = data & 0x80; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + } +} + +READ8_MEMBER(nes_namcot340_device::n340_loread) +{ + LOG_MMC(("n340_loread, offset: %04x\n", offset)); + offset += 0x100; + + switch (offset & 0x1800) + { + case 0x1000: + return m_irq_count & 0xff; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + case 0x1800: + return (m_irq_count >> 8) & 0xff; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + default: + return 0x00; + } +} + +WRITE8_MEMBER(nes_namcot340_device::n340_hiwrite) +{ + LOG_MMC(("n340_hiwrite, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7800) + { + case 0x0000: case 0x0800: + case 0x1000: case 0x1800: + case 0x2000: case 0x2800: + case 0x3000: case 0x3800: + chr1_x(offset / 0x800, data, CHRROM); + break; + case 0x4000: + // no cart found with wram, so it is not clear if this could work as in Namcot-175... + break; + case 0x6000: + switch (data & 0xc0) + { + case 0x00: + set_nt_mirroring(PPU_MIRROR_LOW); + break; + case 0x40: + set_nt_mirroring(PPU_MIRROR_VERT); + break; + case 0x80: + set_nt_mirroring(PPU_MIRROR_HIGH); + break; + case 0xc0: + set_nt_mirroring(PPU_MIRROR_HORZ); + break; + } + prg8_89(data & 0x3f); + break; + case 0x6800: + prg8_ab(data & 0x3f); + break; + case 0x7000: + prg8_cd(data & 0x3f); + break; + } +} + + +/*------------------------------------------------- + + Namcot-175 board emulation + + Games: Chibi Maruko-chan, Family Circuit '91, + Famista '91 + + This (and Namcot-340 above) is a cut-down version + of the Namcot-163 chip, without the sound capabilities. + They also cannot use NTRAM as VRAM and differ for + the mirroring handling + + iNES: mapper 210 + + In MESS: Supported + + -------------------------------------------------*/ + +READ8_MEMBER(nes_namcot175_device::read_m) +{ + // the only game supporting this is Family Circuit '91, and it has 2KB of battery + // but it's mirrored up to 8KB (see Sprint Race -> Back Up menu breakage if not) + if (!m_battery.empty() && !m_wram_protect) + return m_battery[offset & (m_battery.size() - 1)]; + + return m_open_bus; // open bus +} + +WRITE8_MEMBER(nes_namcot175_device::write_m) +{ + // the only game supporting this is Family Circuit '91, and it has 2KB of battery + // but it's mirrored up to 8KB (see Sprint Race -> Back Up menu breakage if not) + if (!m_battery.empty() && !m_wram_protect) + m_battery[offset & (m_battery.size() - 1)] = data; +} + +WRITE8_MEMBER(nes_namcot175_device::write_h) +{ + LOG_MMC(("namcot175 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7800) + { + case 0x4000: + m_wram_protect = data & 1; + break; + case 0x6000: + prg8_89(data & 0x3f); + break; + default: + n340_hiwrite(space, offset, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + Namcot-163 board emulation + + Games: Battle Fleet, Famista '90, Megami Tensei II, + Juvei Quest, etc. + + Compared to Namcot-175 here we have mapper controlled + mirroring, NTRAM mapping to VRAM and additional + sound hw inside the chip (currently unemulated) and + some internal RAM. + + iNES: mapper 19 + + In MESS: Supported (with no emulation of the + sound component) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_namcot163_device::chr_w) +{ + int bank = offset >> 10; + + if (!(m_latch & 0x40) && m_chr_bank >= 0xe0) + { + // CIRAM!!! + machine().ui().popup_time(10, "CIRAM mapped to VRAM. Please contact MAMEDevs."); + + if (!m_nt_writable[bank & 0x03]) + return; + m_nt_access[bank & 0x03][offset & 0x3ff] = data; + } + // or ROM, so no write +} + +READ8_MEMBER(nes_namcot163_device::chr_r) +{ + int bank = offset >> 10; + if (!(m_latch & 0x40) && m_chr_bank >= 0xe0) + { + // CIRAM!!! + machine().ui().popup_time(10, "CIRAM mapped to VRAM. Please contact MAMEDevs."); + return m_nt_access[bank & 0x03][offset & 0x3ff]; + } + // or ROM, accessed as usual + return m_chr_access[bank][offset & 0x3ff]; +} + + +READ8_MEMBER(nes_namcot163_device::read_m) +{ + if (!m_battery.empty() && offset < m_battery.size()) + return m_battery[offset & (m_battery.size() - 1)]; + + return m_open_bus; // open bus +} + +WRITE8_MEMBER(nes_namcot163_device::write_m) +{ + // the pcb can separately protect each 2KB chunk of the external wram from writes + int bank = (offset & 0x1800) >> 11; + if (!m_battery.empty() && !BIT(m_wram_protect, bank)) + m_battery[offset & (m_battery.size() - 1)] = data; +} + +WRITE8_MEMBER(nes_namcot163_device::write_l) +{ + LOG_MMC(("namcot163 write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + switch (offset & 0x1800) + { + case 0x0800: + LOG_MMC(("Namcot-163 sound reg write, data: %02x\n", data)); + break; + default: + n340_lowrite(space, offset, data, mem_mask); + break; + } +} + +READ8_MEMBER(nes_namcot163_device::read_l) +{ + LOG_MMC(("namcot163 read_l, offset: %04x\n", offset)); + offset += 0x100; + + switch (offset & 0x1800) + { + case 0x0800: + LOG_MMC(("Namcot-163 sound reg read\n")); + return 0; + default: + return n340_loread(space, offset, mem_mask); + } +} + +void nes_namcot163_device::set_mirror(UINT8 page, UINT8 data) +{ + if (data < 0xe0) + set_nt_page(page, VROM, data, 0); + else + set_nt_page(page, CIRAM, data & 0x01, 1); +} + +WRITE8_MEMBER(nes_namcot163_device::write_h) +{ + int page; + LOG_MMC(("namcot163 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7800) + { + case 0x0000: case 0x0800: + case 0x1000: case 0x1800: + case 0x2000: case 0x2800: + case 0x3000: case 0x3800: + m_chr_bank = data; + chr1_x(offset / 0x800, m_chr_bank, CHRROM); + break; + case 0x4000: + case 0x4800: + case 0x5000: + case 0x5800: + page = (offset & 0x1800) >> 11; + set_mirror(page, data); + break; + case 0x6000: + // TODO: data & 40 (or data & c0) disable sound if set + prg8_89(data & 0x3f); + break; + case 0x6800: + m_latch = data & 0xc0; + prg8_ab(data & 0x3f); + break; + case 0x7800: + // the lower 4 bits work *BOTH* as WRAM write protect *AND* as sound address! + m_wram_protect = data & 0x0f; + LOG_MMC(("Namcot-163 sound address write, data: %02x\n", data)); + break; + default: + n340_hiwrite(space, offset, data, mem_mask); + break; + } +} diff --git a/src/devices/bus/nes/namcot.h b/src/devices/bus/nes/namcot.h new file mode 100644 index 00000000000..268347068b9 --- /dev/null +++ b/src/devices/bus/nes/namcot.h @@ -0,0 +1,163 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_NAMCOT_H +#define __NES_NAMCOT_H + +#include "nxrom.h" + + +// ======================> nes_namcot3433_device + +class nes_namcot3433_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_namcot3433_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_namcot3433_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(dxrom_write); + virtual DECLARE_WRITE8_MEMBER(write_h) { dxrom_write(space, offset, data, mem_mask); } + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_namcot3446_device + +class nes_namcot3446_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_namcot3446_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_namcot3425_device + +class nes_namcot3425_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_namcot3425_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; + UINT8 m_reg[4]; +}; + + +// ======================> nes_namcot340_device + +class nes_namcot340_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_namcot340_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_namcot340_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(n340_loread); + virtual DECLARE_WRITE8_MEMBER(n340_lowrite); + virtual DECLARE_WRITE8_MEMBER(n340_hiwrite); + virtual DECLARE_READ8_MEMBER(read_l) { return n340_loread(space, offset, mem_mask); } + virtual DECLARE_WRITE8_MEMBER(write_l) { n340_lowrite(space, offset, data, mem_mask); } + virtual DECLARE_WRITE8_MEMBER(write_h) { n340_hiwrite(space, offset, data, mem_mask); } + + virtual void pcb_reset(); + +protected: + UINT16 m_irq_count; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; + + // Namcot-163 chip contains 8K of internal ram, possibly battery backed up (not emulated yet) + // was it also present in 175 & 340 chips? + UINT8 m_n163_ram[0x2000]; +}; + + +// ======================> nes_namcot175_device + +class nes_namcot175_device : public nes_namcot340_device +{ +public: + // construction/destruction + nes_namcot175_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_wram_protect; +}; + + +// ======================> nes_namcot163_device + +class nes_namcot163_device : public nes_namcot340_device +{ +public: + // construction/destruction + nes_namcot163_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + // we have to overwrite these to allow CIRAM to be used for VRAM, even if it's not clear which game(s) use this + virtual DECLARE_READ8_MEMBER(chr_r); + virtual DECLARE_WRITE8_MEMBER(chr_w); + + virtual void pcb_reset(); + +private: + void set_mirror(UINT8 page, UINT8 data); + + UINT8 m_wram_protect, m_latch, m_chr_bank; + // TODO: add emulation of the sound part of the chip +}; + + + +// device type definition +extern const device_type NES_NAMCOT3433; +extern const device_type NES_NAMCOT3446; +extern const device_type NES_NAMCOT3425; +extern const device_type NES_NAMCOT175; +extern const device_type NES_NAMCOT340; +extern const device_type NES_NAMCOT163; + +#endif diff --git a/src/devices/bus/nes/nanjing.c b/src/devices/bus/nes/nanjing.c new file mode 100644 index 00000000000..4d0fc022520 --- /dev/null +++ b/src/devices/bus/nes/nanjing.c @@ -0,0 +1,175 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Nanjing PCBs + + TODO: + - Emulate the variants often assigned to mapper 162/164 (and investigate connection with Waixing FS-304)! + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "nanjing.h" + +#include "video/ppu2c0x.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_NANJING = &device_creator; + + +nes_nanjing_device::nes_nanjing_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NANJING, "NES Cart Nanjing PCB", tag, owner, clock, "nes_nanjing", __FILE__) +{ +} + + + + +void nes_nanjing_device::device_start() +{ + common_start(); + save_item(NAME(m_count)); + save_item(NAME(m_latch1)); + save_item(NAME(m_latch2)); + save_item(NAME(m_reg)); +} + +void nes_nanjing_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(m_prg_chunks - 2); + prg16_cdef(m_prg_chunks - 1); + chr8(0, m_chr_source); + + m_count = 0xff; + m_latch1 = 0; + m_latch2 = 0; + m_reg[0] = 0xff; + m_reg[1] = 0; +} + + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Bootleg Board by Nanjing + + Games: A lot of pirate originals + + iNES: mapper 163 + + In MESS: Unsupported. + + -------------------------------------------------*/ + +void nes_nanjing_device::hblank_irq(int scanline, int vblank, int blanked) +{ + if (BIT(m_reg[0], 7)) + { + if (scanline == 127) + { + chr4_0(1, CHRRAM); + chr4_4(1, CHRRAM); + } + + if (scanline == 239) + { + chr4_0(0, CHRRAM); + chr4_4(0, CHRRAM); + } + } + +} + +WRITE8_MEMBER(nes_nanjing_device::write_l) +{ + LOG_MMC(("nanjing write_l, offset: %04x, data: %02x\n", offset, data)); + + offset += 0x100; + + if (offset < 0x1000) + return; + + if (offset == 0x1100) // 0x5100 + { + if (data == 6) + prg32(3); + return; + } + + if (offset == 0x1101) // 0x5101 + { + UINT8 temp = m_count; + m_count = data; + + if (temp & !data) + m_latch2 ^= 0xff; + } + + switch (offset & 0x300) + { + case 0x000: + case 0x200: + m_reg[BIT(offset, 9)] = data; + if (!BIT(m_reg[0], 7) && machine().device("ppu")->get_current_scanline() <= 127) + chr8(0, CHRRAM); + break; + case 0x300: + m_latch1 = data; + break; + } + + prg32((m_reg[0] & 0x0f) | ((m_reg[1] & 0x0f) << 4)); +} + +READ8_MEMBER(nes_nanjing_device::read_l) +{ + UINT8 value = 0; + LOG_MMC(("nanjing read_l, offset: %04x\n", offset)); + + offset += 0x100; + + if (offset < 0x1000) + return 0; + + switch (offset & 0x700) + { + case 0x100: + value = m_latch1; + break; + case 0x500: + value = m_latch2 & m_latch1; + break; + case 0x000: + case 0x200: + case 0x300: + case 0x400: + case 0x600: + case 0x700: + value = 4; + break; + } + return value; +} diff --git a/src/devices/bus/nes/nanjing.h b/src/devices/bus/nes/nanjing.h new file mode 100644 index 00000000000..40fb8c2e714 --- /dev/null +++ b/src/devices/bus/nes/nanjing.h @@ -0,0 +1,38 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_NANJING_H +#define __NES_NANJING_H + +#include "nxrom.h" + + +// ======================> nes_nanjing_device + +class nes_nanjing_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_nanjing_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +private: + UINT8 m_count; + UINT8 m_reg[2]; + UINT8 m_latch1, m_latch2; +}; + + + + + +// device type definition +extern const device_type NES_NANJING; + +#endif diff --git a/src/devices/bus/nes/nes_carts.c b/src/devices/bus/nes/nes_carts.c new file mode 100644 index 00000000000..6acac2fc6e7 --- /dev/null +++ b/src/devices/bus/nes/nes_carts.c @@ -0,0 +1,366 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/********************************************************************** + + NES carts + +**********************************************************************/ + +#include "nes_carts.h" + +SLOT_INTERFACE_START(nes_cart) +// HROM, NROM, RROM, SROM & STROM + SLOT_INTERFACE_INTERNAL("nrom", NES_NROM) +// Nintendo Family BASIC pcb (NROM + 2K or 4K WRAM) + SLOT_INTERFACE_INTERNAL("hvc_basic", NES_FCBASIC) +// Extended NROM-368 board (NROM with 46K PRG) + SLOT_INTERFACE_INTERNAL("nrom368", NES_NROM368) +// Game Genie + SLOT_INTERFACE_INTERNAL("nrom_gg", NES_GGENIE) +// UNROM/UOROM + SLOT_INTERFACE_INTERNAL("uxrom", NES_UXROM) + SLOT_INTERFACE_INTERNAL("unrom_cc", NES_UXROM_CC) +// CNROM + SLOT_INTERFACE_INTERNAL("cnrom", NES_CNROM) +// Bandai PT-554 (CNROM boards + special audio chip, used by Aerobics Studio) + SLOT_INTERFACE_INTERNAL("bandai_pt554", NES_BANDAI_PT554) +// CPROM + SLOT_INTERFACE_INTERNAL("cprom", NES_CPROM) +// AMROM, ANROM, AOROM + SLOT_INTERFACE_INTERNAL("axrom", NES_AXROM) +// PxROM + SLOT_INTERFACE_INTERNAL("pxrom", NES_PXROM) +// FxROM + SLOT_INTERFACE_INTERNAL("fxrom", NES_FXROM) +// BNROM + SLOT_INTERFACE_INTERNAL("bnrom", NES_BXROM) +// GNROM & MHROM + SLOT_INTERFACE_INTERNAL("gxrom", NES_GXROM) +// UN1ROM + SLOT_INTERFACE_INTERNAL("un1rom", NES_UN1ROM) +// SxROM + SLOT_INTERFACE_INTERNAL("sxrom", NES_SXROM) + SLOT_INTERFACE_INTERNAL("sorom", NES_SOROM) + SLOT_INTERFACE_INTERNAL("sxrom_a", NES_SXROM_A) // in MMC1-A PRG RAM is always enabled + SLOT_INTERFACE_INTERNAL("sorom_a", NES_SOROM_A) // in MMC1-A PRG RAM is always enabled +// TxROM + SLOT_INTERFACE_INTERNAL("txrom", NES_TXROM) +// HKROM + SLOT_INTERFACE_INTERNAL("hkrom", NES_HKROM) +// TQROM + SLOT_INTERFACE_INTERNAL("tqrom", NES_TQROM) +// TxSROM + SLOT_INTERFACE_INTERNAL("txsrom", NES_TXSROM) +// ExROM + SLOT_INTERFACE_INTERNAL("exrom", NES_EXROM) +// RAM expansion + Disk System add-on + SLOT_INTERFACE_INTERNAL("disksys", NES_DISKSYS) +// Nintendo Custom boards + SLOT_INTERFACE_INTERNAL("pal_zz", NES_ZZ_PCB) + SLOT_INTERFACE_INTERNAL("nes_qj", NES_QJ_PCB) + SLOT_INTERFACE_INTERNAL("nes_event", NES_EVENT) +// Discrete Components boards +// IC_74x139x74 + SLOT_INTERFACE_INTERNAL("discrete_74x139", NES_74X139X74) +// IC_74x377 + SLOT_INTERFACE_INTERNAL("discrete_74x377", NES_74X377) +// Discrete board IC_74x161x161x32 + SLOT_INTERFACE_INTERNAL("discrete_74x161", NES_74X161X161X32) +// Discrete board IC_74x161x138 + SLOT_INTERFACE_INTERNAL("bitcorp_dis", NES_74X161X138) +// Bandai boards + SLOT_INTERFACE_INTERNAL("lz93d50", NES_LZ93D50) + SLOT_INTERFACE_INTERNAL("lz93d50_ep1", NES_LZ93D50_24C01) + SLOT_INTERFACE_INTERNAL("lz93d50_ep2", NES_LZ93D50_24C02) + SLOT_INTERFACE_INTERNAL("fcg", NES_FCG) + SLOT_INTERFACE_INTERNAL("fjump2", NES_FJUMP2) + SLOT_INTERFACE_INTERNAL("datach", NES_DATACH) + SLOT_INTERFACE_INTERNAL("karastudio", NES_KARAOKESTUDIO) + SLOT_INTERFACE_INTERNAL("oekakids", NES_OEKAKIDS) +// Irem boards + SLOT_INTERFACE_INTERNAL("g101", NES_G101) + SLOT_INTERFACE_INTERNAL("lrog017", NES_LROG017) + SLOT_INTERFACE_INTERNAL("h3001", NES_H3001) + SLOT_INTERFACE_INTERNAL("holydivr", NES_HOLYDIVR) + SLOT_INTERFACE_INTERNAL("tam_s1", NES_TAM_S1) +// Jaleco boards + SLOT_INTERFACE_INTERNAL("jf11", NES_JF11) + SLOT_INTERFACE_INTERNAL("jf13", NES_JF13) + SLOT_INTERFACE_INTERNAL("jf16", NES_JF16) + SLOT_INTERFACE_INTERNAL("jf17", NES_JF17) + SLOT_INTERFACE_INTERNAL("jf17pcm", NES_JF17_ADPCM) + SLOT_INTERFACE_INTERNAL("jf19", NES_JF19) + SLOT_INTERFACE_INTERNAL("jf19pcm", NES_JF19_ADPCM) + SLOT_INTERFACE_INTERNAL("ss88006", NES_SS88006) + SLOT_INTERFACE_INTERNAL("jf23", NES_JF23) + SLOT_INTERFACE_INTERNAL("jf24", NES_JF24) + SLOT_INTERFACE_INTERNAL("jf29", NES_JF29) + SLOT_INTERFACE_INTERNAL("jf33", NES_JF33) +// Konami boards + SLOT_INTERFACE_INTERNAL("vrc1", NES_VRC1) + SLOT_INTERFACE_INTERNAL("vrc2", NES_VRC2) + SLOT_INTERFACE_INTERNAL("vrc3", NES_VRC3) + SLOT_INTERFACE_INTERNAL("vrc4", NES_VRC4) + SLOT_INTERFACE_INTERNAL("vrc6", NES_VRC6) + SLOT_INTERFACE_INTERNAL("vrc7", NES_VRC7) +// Namcot boards + SLOT_INTERFACE_INTERNAL("namcot_163", NES_NAMCOT163) + SLOT_INTERFACE_INTERNAL("namcot_175", NES_NAMCOT175) + SLOT_INTERFACE_INTERNAL("namcot_340", NES_NAMCOT340) + SLOT_INTERFACE_INTERNAL("namcot_3433", NES_NAMCOT3433) // DxROM is a Nintendo board for US versions of the 3433/3443 games + SLOT_INTERFACE_INTERNAL("namcot_3425", NES_NAMCOT3425) + SLOT_INTERFACE_INTERNAL("namcot_3446", NES_NAMCOT3446) +// Sunsoft boards + SLOT_INTERFACE_INTERNAL("sunsoft1", NES_SUNSOFT_1) + SLOT_INTERFACE_INTERNAL("sunsoft2", NES_SUNSOFT_2) + SLOT_INTERFACE_INTERNAL("sunsoft3", NES_SUNSOFT_3) + SLOT_INTERFACE_INTERNAL("sunsoft4", NES_SUNSOFT_4) + SLOT_INTERFACE_INTERNAL("sunsoft_dcs", NES_SUNSOFT_DCS) + SLOT_INTERFACE_INTERNAL("sunsoft_fme7", NES_SUNSOFT_FME7) // JxROM is a Nintendo board for US versions of the Sunsoft FME7 games + SLOT_INTERFACE_INTERNAL("sunsoft5a", NES_SUNSOFT_5) + SLOT_INTERFACE_INTERNAL("sunsoft5b", NES_SUNSOFT_5) +// Taito boards + SLOT_INTERFACE_INTERNAL("tc0190fmc", NES_TC0190FMC) + SLOT_INTERFACE_INTERNAL("tc0190fmcp", NES_TC0190FMC_PAL16R4) + SLOT_INTERFACE_INTERNAL("tc0350fmr", NES_TC0190FMC) + SLOT_INTERFACE_INTERNAL("x1_005", NES_X1_005) // two variants exist, depending on pin17 & pin31 connections + SLOT_INTERFACE_INTERNAL("x1_017", NES_X1_017) +// Misc pirate boards (by AVE, Camerica, C&E, Nanjing, NTDEC, JY Company, Sachen, Tengen, TXC, Waixing, Henggendianzi, etc.) + SLOT_INTERFACE_INTERNAL("nina001", NES_NINA001) + SLOT_INTERFACE_INTERNAL("nina006", NES_NINA006) + SLOT_INTERFACE_INTERNAL("bf9093", NES_BF9093) + SLOT_INTERFACE_INTERNAL("bf9096", NES_BF9096) + SLOT_INTERFACE_INTERNAL("goldenfive", NES_GOLDEN5) + SLOT_INTERFACE_INTERNAL("ade", NES_ALADDIN) + SLOT_INTERFACE_INTERNAL("cne_decathl", NES_CNE_DECATHL) + SLOT_INTERFACE_INTERNAL("cne_fsb", NES_CNE_FSB) + SLOT_INTERFACE_INTERNAL("cne_shlz", NES_CNE_SHLZ) + SLOT_INTERFACE_INTERNAL("nanjing", NES_NANJING) // mapper 163 + SLOT_INTERFACE_INTERNAL("ntdec_asder", NES_NTDEC_ASDER) // mapper 112 + SLOT_INTERFACE_INTERNAL("ntdec_fh", NES_NTDEC_FH) // mapper 193 + SLOT_INTERFACE_INTERNAL("jyc_a", NES_JY_TYPEA) // mapper 90 + SLOT_INTERFACE_INTERNAL("jyc_b", NES_JY_TYPEB) // mapper 211 + SLOT_INTERFACE_INTERNAL("jyc_c", NES_JY_TYPEC) // mapper 209 + SLOT_INTERFACE_INTERNAL("sa009", NES_SACHEN_SA009) + SLOT_INTERFACE_INTERNAL("sa0036", NES_SACHEN_SA0036) + SLOT_INTERFACE_INTERNAL("sa0037", NES_SACHEN_SA0037) + SLOT_INTERFACE_INTERNAL("sa72007", NES_SACHEN_SA72007) + SLOT_INTERFACE_INTERNAL("sa72008", NES_SACHEN_SA72008) + SLOT_INTERFACE_INTERNAL("tca01", NES_SACHEN_TCA01) + SLOT_INTERFACE_INTERNAL("s8259a", NES_SACHEN_8259A) + SLOT_INTERFACE_INTERNAL("s8259b", NES_SACHEN_8259B) + SLOT_INTERFACE_INTERNAL("s8259c", NES_SACHEN_8259C) + SLOT_INTERFACE_INTERNAL("s8259d", NES_SACHEN_8259D) + SLOT_INTERFACE_INTERNAL("s74x374", NES_SACHEN_74X374) + SLOT_INTERFACE_INTERNAL("s74x374a", NES_SACHEN_74X374_ALT) /* FIXME: Made up boards some different handling */ + SLOT_INTERFACE_INTERNAL("tcu01", NES_SACHEN_TCU01) + SLOT_INTERFACE_INTERNAL("tcu02", NES_SACHEN_TCU02) + SLOT_INTERFACE_INTERNAL("tengen_800008", NES_TENGEN_800008) /* FIXME: Is this the same as CNROM? */ + SLOT_INTERFACE_INTERNAL("tengen_800032", NES_TENGEN_800032) + SLOT_INTERFACE_INTERNAL("tengen_800037", NES_TENGEN_800037) + SLOT_INTERFACE_INTERNAL("txc_22211", NES_TXC_22211) + SLOT_INTERFACE_INTERNAL("txc_dumarc", NES_TXC_DUMARACING) + SLOT_INTERFACE_INTERNAL("txc_mjblock", NES_TXC_MJBLOCK) + SLOT_INTERFACE_INTERNAL("txc_strikew", NES_TXC_STRIKEW) + SLOT_INTERFACE_INTERNAL("txc_commandos", NES_TXC_COMMANDOS) + SLOT_INTERFACE_INTERNAL("waixing_a", NES_WAIXING_A) + SLOT_INTERFACE_INTERNAL("waixing_a1", NES_WAIXING_A1) /* FIXME: Made up boards the different CHRRAM banks (see Ji Jia Zhan Shi) */ + SLOT_INTERFACE_INTERNAL("waixing_b", NES_WAIXING_B) + SLOT_INTERFACE_INTERNAL("waixing_c", NES_WAIXING_C) + SLOT_INTERFACE_INTERNAL("waixing_d", NES_WAIXING_D) + SLOT_INTERFACE_INTERNAL("waixing_e", NES_WAIXING_E) + SLOT_INTERFACE_INTERNAL("waixing_f", NES_WAIXING_F) + SLOT_INTERFACE_INTERNAL("waixing_g", NES_WAIXING_G) + SLOT_INTERFACE_INTERNAL("waixing_h", NES_WAIXING_H) + SLOT_INTERFACE_INTERNAL("waixing_h1", NES_WAIXING_H1) /* FIXME: Made up boards the different WRAM protect banks (see Shen Mi Jin San Jiao) */ + SLOT_INTERFACE_INTERNAL("waixing_i", NES_WAIXING_I) + SLOT_INTERFACE_INTERNAL("waixing_j", NES_WAIXING_J) + SLOT_INTERFACE_INTERNAL("waixing_sgz", NES_WAIXING_SGZ) + SLOT_INTERFACE_INTERNAL("waixing_sgzlz", NES_WAIXING_SGZLZ) + SLOT_INTERFACE_INTERNAL("waixing_sec", NES_WAIXING_SEC) + SLOT_INTERFACE_INTERNAL("waixing_ffv", NES_WAIXING_FFV) + SLOT_INTERFACE_INTERNAL("waixing_wxzs", NES_WAIXING_WXZS) + SLOT_INTERFACE_INTERNAL("waixing_wxzs2", NES_WAIXING_WXZS2) + SLOT_INTERFACE_INTERNAL("waixing_dq8", NES_WAIXING_DQ8) + SLOT_INTERFACE_INTERNAL("waixing_sh2", NES_WAIXING_SH2) + SLOT_INTERFACE_INTERNAL("fs304", NES_WAIXING_FS304) // used in Zelda 3 by Waixing + SLOT_INTERFACE_INTERNAL("cony", NES_CONY) + SLOT_INTERFACE_INTERNAL("yoko", NES_YOKO) + SLOT_INTERFACE_INTERNAL("hengg_srich", NES_HENGG_SRICH) + SLOT_INTERFACE_INTERNAL("hengg_xhzs", NES_HENGG_XHZS) + SLOT_INTERFACE_INTERNAL("hengg_shjy3", NES_HENGG_SHJY3) // mapper 253 + SLOT_INTERFACE_INTERNAL("hes", NES_HES) + SLOT_INTERFACE_INTERNAL("hosenkan", NES_HOSENKAN) + SLOT_INTERFACE_INTERNAL("ks7058", NES_KS7058) + SLOT_INTERFACE_INTERNAL("ks202", NES_KS202) // mapper 56 + SLOT_INTERFACE_INTERNAL("ks7022", NES_KS7022)// mapper 175 + SLOT_INTERFACE_INTERNAL("ks7017", NES_KS7017) + SLOT_INTERFACE_INTERNAL("ks7032", NES_KS7032) // mapper 142 + SLOT_INTERFACE_INTERNAL("ks7012", NES_KS7012) // used in Zanac (FDS Conversion) + SLOT_INTERFACE_INTERNAL("ks7013b", NES_KS7013B) // used in Highway Star (FDS Conversion) + SLOT_INTERFACE_INTERNAL("ks7031", NES_KS7031) // used in Dracula II (FDS Conversion) + SLOT_INTERFACE_INTERNAL("gs2015", NES_GS2015) + SLOT_INTERFACE_INTERNAL("gs2004", NES_GS2004) + SLOT_INTERFACE_INTERNAL("gs2013", NES_GS2013) + SLOT_INTERFACE_INTERNAL("tf9in1", NES_TF9IN1) + SLOT_INTERFACE_INTERNAL("3dblock", NES_3DBLOCK) // NROM + IRQ? + SLOT_INTERFACE_INTERNAL("racermate", NES_RACERMATE) // mapper 168 + SLOT_INTERFACE_INTERNAL("agci_50282", NES_AGCI_50282) + SLOT_INTERFACE_INTERNAL("dreamtech01", NES_DREAMTECH01) + SLOT_INTERFACE_INTERNAL("fukutake", NES_FUKUTAKE) + SLOT_INTERFACE_INTERNAL("futuremedia", NES_FUTUREMEDIA) + SLOT_INTERFACE_INTERNAL("magicseries", NES_MAGSERIES) + SLOT_INTERFACE_INTERNAL("daou_306", NES_DAOU306) + SLOT_INTERFACE_INTERNAL("subor0", NES_SUBOR0) + SLOT_INTERFACE_INTERNAL("subor1", NES_SUBOR1) + SLOT_INTERFACE_INTERNAL("cc21", NES_CC21) + SLOT_INTERFACE_INTERNAL("xiaozy", NES_XIAOZY) + SLOT_INTERFACE_INTERNAL("edu2k", NES_EDU2K) + SLOT_INTERFACE_INTERNAL("t230", NES_T230) + SLOT_INTERFACE_INTERNAL("mk2", NES_MK2) + SLOT_INTERFACE_INTERNAL("unl_whero", NES_WHERO) // mapper 27 + SLOT_INTERFACE_INTERNAL("unl_43272", NES_43272) // used in Gaau Hok Gwong Cheung + SLOT_INTERFACE_INTERNAL("tf1201", NES_TF1201) + SLOT_INTERFACE_INTERNAL("unl_cfight", NES_CITYFIGHT) // used by City Fighter IV +// misc bootleg boards + SLOT_INTERFACE_INTERNAL("ax5705", NES_AX5705) + SLOT_INTERFACE_INTERNAL("sc127", NES_SC127) + SLOT_INTERFACE_INTERNAL("mariobaby", NES_MARIOBABY) + SLOT_INTERFACE_INTERNAL("asnicol", NES_ASN) + SLOT_INTERFACE_INTERNAL("smb3pirate", NES_SMB3PIRATE) + SLOT_INTERFACE_INTERNAL("btl_dninja", NES_BTL_DNINJA) + SLOT_INTERFACE_INTERNAL("whirl2706", NES_WHIRLWIND_2706) + SLOT_INTERFACE_INTERNAL("smb2j", NES_SMB2J) + SLOT_INTERFACE_INTERNAL("smb2ja", NES_SMB2JA) + SLOT_INTERFACE_INTERNAL("smb2jb", NES_SMB2JB) + SLOT_INTERFACE_INTERNAL("09034a", NES_09034A) + SLOT_INTERFACE_INTERNAL("tobidase", NES_TOBIDASE) // mapper 120 + SLOT_INTERFACE_INTERNAL("mmalee2", NES_MMALEE) // mapper 55? + SLOT_INTERFACE_INTERNAL("unl_2708", NES_2708) // mapper 103 + SLOT_INTERFACE_INTERNAL("unl_lh32", NES_LH32) // used by Monty no Doki Doki Daidassou FDS conversion + SLOT_INTERFACE_INTERNAL("unl_lh10", NES_LH10) // used in Fuuun Shaolin Kyo (FDS Conversion) + SLOT_INTERFACE_INTERNAL("unl_lh53", NES_LH53) // used in Nazo no Murasamejou (FDS Conversion) + SLOT_INTERFACE_INTERNAL("unl_ac08", NES_AC08) // used by Green Beret FDS conversion + SLOT_INTERFACE_INTERNAL("unl_bb", NES_UNL_BB) // used by a few FDS conversions + SLOT_INTERFACE_INTERNAL("sgpipe", NES_SHUIGUAN) // mapper 183 +// misc MMC3 clone boards + SLOT_INTERFACE_INTERNAL("dbz5", NES_REX_DBZ5) + SLOT_INTERFACE_INTERNAL("sl1632", NES_REX_SL1632) + SLOT_INTERFACE_INTERNAL("somari", NES_SOMARI) // mapper 116 + SLOT_INTERFACE_INTERNAL("nitra", NES_NITRA) + SLOT_INTERFACE_INTERNAL("ks7057", NES_KS7057) // mapper 196 alt (for Street Fighter VI / Fight Street VI) + SLOT_INTERFACE_INTERNAL("sbros11", NES_SBROS11) + SLOT_INTERFACE_INTERNAL("unl_malisb", NES_MALISB) // used by Super Mali Splash Bomb + SLOT_INTERFACE_INTERNAL("family4646", NES_FAMILY4646) + SLOT_INTERFACE_INTERNAL("pikay2k", NES_PIKAY2K) // mapper 254 + SLOT_INTERFACE_INTERNAL("8237", NES_8237) + SLOT_INTERFACE_INTERNAL("8237a", NES_NROM) // UNSUPPORTED + SLOT_INTERFACE_INTERNAL("sg_lionk", NES_SG_LIONK) + SLOT_INTERFACE_INTERNAL("sg_boog", NES_SG_BOOG) + SLOT_INTERFACE_INTERNAL("kasing", NES_KASING) + SLOT_INTERFACE_INTERNAL("kay", NES_KAY) + SLOT_INTERFACE_INTERNAL("h2288", NES_H2288) + SLOT_INTERFACE_INTERNAL("unl_6035052", NES_6035052) // mapper 238? + SLOT_INTERFACE_INTERNAL("txc_tw", NES_TXC_TW) + SLOT_INTERFACE_INTERNAL("kof97", NES_KOF97) + SLOT_INTERFACE_INTERNAL("kof96", NES_KOF96) + SLOT_INTERFACE_INTERNAL("sfight3", NES_SF3) + SLOT_INTERFACE_INTERNAL("gouder", NES_GOUDER) + SLOT_INTERFACE_INTERNAL("sa9602b", NES_SA9602B) + SLOT_INTERFACE_INTERNAL("unl_shero", NES_SACHEN_SHERO) +// misc multigame cart boards + SLOT_INTERFACE_INTERNAL("benshieng", NES_BENSHIENG) + SLOT_INTERFACE_INTERNAL("action52", NES_ACTION52) + SLOT_INTERFACE_INTERNAL("caltron6in1", NES_CALTRON6IN1) + SLOT_INTERFACE_INTERNAL("maxi15", NES_MAXI15) // mapper 234 + SLOT_INTERFACE_INTERNAL("rumblestation", NES_RUMBLESTATION) // mapper 46 + SLOT_INTERFACE_INTERNAL("svision16", NES_SVISION16) // mapper 53 + SLOT_INTERFACE_INTERNAL("n625092", NES_N625092) + SLOT_INTERFACE_INTERNAL("a65as", NES_A65AS) + SLOT_INTERFACE_INTERNAL("t262", NES_T262) + SLOT_INTERFACE_INTERNAL("novel1", NES_NOVEL1) + SLOT_INTERFACE_INTERNAL("novel2", NES_NOVEL2) // mapper 213... same as BMC-NOVELDIAMOND9999999IN1 board? + SLOT_INTERFACE_INTERNAL("studyngame", NES_STUDYNGAME) // mapper 39 + SLOT_INTERFACE_INTERNAL("sgun20in1", NES_SUPERGUN20IN1) + SLOT_INTERFACE_INTERNAL("bmc_vt5201", NES_VT5201) // mapper 60 otherwise + SLOT_INTERFACE_INTERNAL("bmc_d1038", NES_VT5201) // mapper 60? + SLOT_INTERFACE_INTERNAL("810544c", NES_810544C) + SLOT_INTERFACE_INTERNAL("ntd03", NES_NTD03) + SLOT_INTERFACE_INTERNAL("bmc_gb63", NES_BMC_GB63) + SLOT_INTERFACE_INTERNAL("bmc_gka", NES_BMC_GKA) + SLOT_INTERFACE_INTERNAL("bmc_gkb", NES_BMC_GKB) + SLOT_INTERFACE_INTERNAL("bmc_ws", NES_BMC_WS) + SLOT_INTERFACE_INTERNAL("bmc_g146", NES_BMC_G146) + SLOT_INTERFACE_INTERNAL("bmc_11160", NES_BMC_11160) + SLOT_INTERFACE_INTERNAL("bmc_8157", NES_BMC_8157) + SLOT_INTERFACE_INTERNAL("bmc_hik300", NES_BMC_HIK300) + SLOT_INTERFACE_INTERNAL("bmc_s700", NES_BMC_S700) + SLOT_INTERFACE_INTERNAL("bmc_ball11", NES_BMC_BALL11) + SLOT_INTERFACE_INTERNAL("bmc_22games", NES_BMC_22GAMES) + SLOT_INTERFACE_INTERNAL("bmc_64y2k", NES_BMC_64Y2K) + SLOT_INTERFACE_INTERNAL("bmc_12in1", NES_BMC_12IN1) + SLOT_INTERFACE_INTERNAL("bmc_20in1", NES_BMC_20IN1) + SLOT_INTERFACE_INTERNAL("bmc_21in1", NES_BMC_21IN1) + SLOT_INTERFACE_INTERNAL("bmc_31in1", NES_BMC_31IN1) + SLOT_INTERFACE_INTERNAL("bmc_35in1", NES_BMC_35IN1) + SLOT_INTERFACE_INTERNAL("bmc_36in1", NES_BMC_36IN1) + SLOT_INTERFACE_INTERNAL("bmc_64in1", NES_BMC_64IN1) + SLOT_INTERFACE_INTERNAL("bmc_70in1", NES_BMC_70IN1) // mapper 236? + SLOT_INTERFACE_INTERNAL("bmc_72in1", NES_BMC_72IN1) + SLOT_INTERFACE_INTERNAL("bmc_76in1", NES_BMC_76IN1) + SLOT_INTERFACE_INTERNAL("bmc_s42in1", NES_BMC_76IN1) + SLOT_INTERFACE_INTERNAL("bmc_110in1", NES_BMC_110IN1) + SLOT_INTERFACE_INTERNAL("bmc_150in1", NES_BMC_150IN1) + SLOT_INTERFACE_INTERNAL("bmc_190in1", NES_BMC_190IN1) + SLOT_INTERFACE_INTERNAL("bmc_800in1", NES_BMC_800IN1) // mapper 236? + SLOT_INTERFACE_INTERNAL("bmc_1200in1", NES_BMC_1200IN1) + SLOT_INTERFACE_INTERNAL("bmc_gold150", NES_BMC_GOLD150) // mapper 235 with 2M PRG + SLOT_INTERFACE_INTERNAL("bmc_gold260", NES_BMC_GOLD260) // mapper 235 with 4M PRG + SLOT_INTERFACE_INTERNAL("bmc_power255", NES_BMC_CH001) // mapper 63? + SLOT_INTERFACE_INTERNAL("bmc_s22games", NES_BMC_SUPER22) // mapper 233 + SLOT_INTERFACE_INTERNAL("bmc_reset4", NES_BMC_4IN1RESET) // mapper 60 with 64k prg and 32k chr + SLOT_INTERFACE_INTERNAL("bmc_reset42", NES_BMC_42IN1RESET) // mapper 60? or 226? or 233? +// misc multigame cart MMC3 clone boards + SLOT_INTERFACE_INTERNAL("fk23c", NES_FK23C) + SLOT_INTERFACE_INTERNAL("fk23ca", NES_FK23CA) + SLOT_INTERFACE_INTERNAL("s24in1c03", NES_S24IN1SC03) + SLOT_INTERFACE_INTERNAL("bmc_15in1", NES_BMC_15IN1) + SLOT_INTERFACE_INTERNAL("bmc_sbig7in1", NES_BMC_SBIG7) + SLOT_INTERFACE_INTERNAL("bmc_hik8in1", NES_BMC_HIK8) + SLOT_INTERFACE_INTERNAL("bmc_hik4in1", NES_BMC_HIK4) + SLOT_INTERFACE_INTERNAL("bmc_mario7in1", NES_BMC_MARIO7IN1) + SLOT_INTERFACE_INTERNAL("bmc_gold7in1", NES_BMC_GOLD7IN1) + SLOT_INTERFACE_INTERNAL("bmc_gc6in1", NES_BMC_GC6IN1) + SLOT_INTERFACE_INTERNAL("bmc_411120c", NES_BMC_411120C) + SLOT_INTERFACE_INTERNAL("bmc_830118c", NES_BMC_830118C) + SLOT_INTERFACE_INTERNAL("pjoy84", NES_PJOY84) + SLOT_INTERFACE_INTERNAL("nocash_nochr", NES_NOCHR) + SLOT_INTERFACE_INTERNAL("nes_action53", NES_ACTION53) + SLOT_INTERFACE_INTERNAL("nes_2a03pur", NES_2A03PURITANS) +// other unsupported... + SLOT_INTERFACE_INTERNAL("ninjaryu", NES_NROM) // mapper 111 - UNSUPPORTED + SLOT_INTERFACE_INTERNAL("unl_dance", NES_NROM) // UNSUPPORTED + SLOT_INTERFACE_INTERNAL("onebus", NES_NROM) // UNSUPPORTED + SLOT_INTERFACE_INTERNAL("dance2k", NES_NROM) // UNSUPPORTED + SLOT_INTERFACE_INTERNAL("pec586", NES_NROM) // UNSUPPORTED +// are there dumps of games with these boards? + SLOT_INTERFACE_INTERNAL("bmc_hik_kof", NES_NROM) // mapper 251 - UNSUPPORTED + SLOT_INTERFACE_INTERNAL("bmc_13in1jy110", NES_NROM) // [mentioned in FCEUMM source - we need more info] - UNSUPPORTED + SLOT_INTERFACE_INTERNAL("bmc_gk_192", NES_NROM) // [mentioned in FCEUMM source - we need more info] - UNSUPPORTED + SLOT_INTERFACE_INTERNAL("konami_qtai", NES_NROM) // [mentioned in FCEUMM source - we need more info] - UNSUPPORTED + SLOT_INTERFACE_INTERNAL("unl_3d_block", NES_NROM) // [mentioned in FCEUMM source - we need more info] - UNSUPPORTED + SLOT_INTERFACE_INTERNAL("unl_c_n22m", NES_NROM) // [mentioned in FCEUMM source - we need more info] - UNSUPPORTED + SLOT_INTERFACE_INTERNAL("a9746", NES_NROM) // mapper 219 - UNSUPPORTED (no dump available) +// legacy boards for FFE copier mappers (are there images available to fix/improve emulation?) + SLOT_INTERFACE_INTERNAL("ffe3", NES_FFE3) + SLOT_INTERFACE_INTERNAL("ffe4", NES_FFE4) + SLOT_INTERFACE_INTERNAL("ffe8", NES_FFE8) + SLOT_INTERFACE_INTERNAL("test", NES_NROM) +// + SLOT_INTERFACE_INTERNAL("unknown", NES_NROM) // a few pirate dumps uses the wrong mapper... +SLOT_INTERFACE_END + +SLOT_INTERFACE_START(disksys_only) + // RAM expansion + Disk System add-on + SLOT_INTERFACE("disksys", NES_DISKSYS) +SLOT_INTERFACE_END diff --git a/src/devices/bus/nes/nes_carts.h b/src/devices/bus/nes/nes_carts.h new file mode 100644 index 00000000000..144d7aa0a8e --- /dev/null +++ b/src/devices/bus/nes/nes_carts.h @@ -0,0 +1,73 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/********************************************************************** + + NES carts + +**********************************************************************/ + +#pragma once + +#ifndef __NES_CARTS_H__ +#define __NES_CARTS_H__ + +#include "emu.h" + +// official PCBs +#include "nxrom.h" +#include "mmc1.h" +#include "mmc2.h" +#include "mmc3.h" +#include "mmc5.h" +#include "bandai.h" +#include "datach.h" +#include "discrete.h" +#include "disksys.h" +#include "event.h" +#include "irem.h" +#include "jaleco.h" +#include "karastudio.h" +#include "konami.h" +#include "namcot.h" +#include "pt554.h" +#include "sunsoft.h" +#include "sunsoft_dcs.h" +#include "taito.h" +// unlicensed/bootleg/pirate PCBs +#include "2a03pur.h" +#include "act53.h" +#include "aladdin.h" +#include "ave.h" +#include "benshieng.h" +#include "camerica.h" +#include "cne.h" +#include "cony.h" +#include "ggenie.h" +#include "hes.h" +#include "henggedianzi.h" +#include "hosenkan.h" +#include "jy.h" +#include "kaiser.h" +#include "legacy.h" +#include "nanjing.h" +#include "ntdec.h" +#include "racermate.h" +#include "rcm.h" +#include "rexsoft.h" +#include "sachen.h" +#include "somari.h" +#include "tengen.h" +#include "txc.h" +#include "waixing.h" +// misc unlicensed/bootleg/pirate PCBs +#include "bootleg.h" +#include "multigame.h" +#include "pirate.h" +#include "mmc3_clones.h" + + +// supported devices +SLOT_INTERFACE_EXTERN(nes_cart); +SLOT_INTERFACE_EXTERN(disksys_only); + +#endif // __NES_CARTS_H__ diff --git a/src/devices/bus/nes/nes_ines.inc b/src/devices/bus/nes/nes_ines.inc new file mode 100644 index 00000000000..ceb9581b82f --- /dev/null +++ b/src/devices/bus/nes/nes_ines.inc @@ -0,0 +1,939 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/***************************************************************************************** + + NES MMC Emulation + + Support for iNES Mappers + +****************************************************************************************/ + + +/* Set to generate prg & chr files when the cart is loaded */ +#define SPLIT_PRG 0 +#define SPLIT_CHR 0 + + +/************************************************************* + + mmc_list + + Supported mappers and corresponding pcb id + + *************************************************************/ + +struct nes_mmc +{ + int iNesMapper; /* iNES Mapper # */ + int pcb_id; +}; + + +static const nes_mmc mmc_list[] = +{ +/* INES DESC LOW_W, LOW_R, MED_W, HIGH_W, PPU_latch, scanline CB, hblank CB */ + { 0, STD_NROM }, + { 1, STD_SXROM }, + { 2, STD_UXROM }, + { 3, STD_CNROM }, + { 4, STD_TXROM }, + { 5, STD_EXROM }, + { 6, FFE4_BOARD }, + { 7, STD_AXROM }, + { 8, FFE3_BOARD }, + { 9, STD_PXROM }, + { 10, STD_FXROM }, + { 11, DIS_74X377 }, + { 12, REXSOFT_DBZ5 }, + { 13, STD_CPROM }, + { 14, REXSOFT_SL1632 }, + { 15, WAIXING_WXZS2 }, + { 16, BANDAI_LZ93EX2 }, // with 24c02 + { 17, FFE8_BOARD }, + { 18, JALECO_SS88006 }, + { 19, NAMCOT_163 }, + { 21, KONAMI_VRC4 }, + { 22, KONAMI_VRC2 }, + { 23, KONAMI_VRC2 }, + { 24, KONAMI_VRC6 }, + { 25, KONAMI_VRC4 }, + { 26, KONAMI_VRC6 }, + { 27, UNL_WORLDHERO }, // 27 World Hero board - Unsupported + { 28, BTL_ACTION53 }, // 28 - Multi-discrete PCB designed by Tepples for Action 53 + // 29 Unused + // 30 UNROM 512 + Flash, currently unsupported + { 31, BTL_2A03_PURITANS }, // 31 - PCB designed by infinitelives & rainwarrior for 2A03 Puritans Album + { 32, IREM_G101 }, + { 33, TAITO_TC0190FMC }, + { 34, STD_BXROM }, + { 35, UNL_SC127 }, + { 36, TXC_STRIKEW }, + { 37, PAL_ZZ }, + { 38, DIS_74X161X138 }, + { 39, UNL_STUDYNGAME }, + { 40, BTL_SMB2JA }, + { 41, CALTRON_6IN1 }, + { 42, BTL_MARIOBABY }, // ai senshi nicole too, changed by crc_hack + { 43, UNL_SMB2J }, + { 44, BMC_SUPERBIG_7IN1 }, + { 45, BMC_HIK8IN1 }, + { 46, RUMBLESTATION_BOARD }, + { 47, NES_QJ }, + { 48, TAITO_TC0190FMCP }, + { 49, BMC_SUPERHIK_4IN1 }, + { 50, BTL_SMB2JB }, + { 51, BMC_BALLGAMES_11IN1 }, + { 52, BMC_GOLD_7IN1 }, + { 53, SVISION16_BOARD }, + { 54, BMC_NOVEL1 }, + // 55 Genius SMB - No info (nor images) available + { 56, KAISER_KS202 }, + { 57, BMC_GKA }, + { 58, BMC_GKB }, + // 59 Unused + // 60 4-in-1, 35-in-1 Reset based + { 61, RCM_TF9IN1 }, + { 62, BMC_SUPER_700IN1 }, + { 63, BMC_CH001 }, // Powerful 255 + { 64, TENGEN_800032 }, + { 65, IREM_H3001 }, + { 66, STD_GXROM }, + { 67, SUNSOFT_3 }, + { 68, SUNSOFT_DCS }, + { 69, SUNSOFT_FME7 }, + { 70, DIS_74X161X161X32 }, + { 71, CAMERICA_BF9093 }, + { 72, JALECO_JF17 }, + { 73, KONAMI_VRC3 }, + { 74, WAIXING_TYPE_A }, + { 75, KONAMI_VRC1 }, + { 76, NAMCOT_3446 }, + { 77, IREM_LROG017 }, + { 78, IREM_HOLYDIVR }, + { 79, AVE_NINA06 }, + { 80, TAITO_X1_005 }, + // 81 Unused + { 82, TAITO_X1_017 }, + { 83, CONY_BOARD }, + // 84 Pasofami hacked images? + { 85, KONAMI_VRC7 }, + { 86, JALECO_JF13 }, + { 87, DIS_74X139X74 }, + { 88, NAMCOT_34X3 }, + { 89, SUNSOFT_2 }, + { 90, JYCOMPANY_A }, + { 91, UNL_MK2 }, + { 92, JALECO_JF19 }, + { 93, SUNSOFT_2 }, + { 94, STD_UN1ROM }, + { 95, NAMCOT_3425 }, + { 96, BANDAI_OEKAKIDS }, + { 97, IREM_TAM_S1 }, + // 98 Unused + // 99 VS. system - Not going to be implemented (use MAME instead) + // 100 images hacked to work with nesticle? + // 101 Unused (Urusei Yatsura had been assigned to this mapper, but it's Mapper 87) + // 102 Unused + { 103, UNL_2708 }, // 103 Bootleg cart 2708 (Doki Doki Panic - FDS Conversion) - Unsupported + { 104, CAMERICA_GOLDENFIVE }, + { 105, STD_EVENT }, + { 106, BTL_SMB3 }, + { 107, MAGICSERIES_MD }, + { 108, WHIRLWIND_2706 }, + // 109 Unused + // 110 Unused + // 111 Ninja Ryuukenden Chinese? - Unsupported + { 112, NTDEC_ASDER }, + { 113, HES_BOARD }, + { 114, SUPERGAME_LIONKING }, + { 115, KASING_BOARD }, + { 116, SOMARI_SL12 }, + { 117, FUTUREMEDIA_BOARD }, + { 118, STD_TXSROM }, + { 119, STD_TQROM }, + { 120, BTL_TOBIDASE }, + { 121, KAY_BOARD }, + // 122 Unused + { 123, UNL_H2288 }, + // 124 Unused + // 125 Unused + { 126, BMC_PJOY84 }, + // 127 Unused + // 128 Unused + // 129 Unused + // 130 Unused + // 131 Unused + { 132, TXC_22211 }, + { 133, SACHEN_SA72008 }, + { 134, BMC_FAMILY_4646 }, + // 135 Unused + { 136, SACHEN_TCU02 }, + { 137, SACHEN_8259D }, + { 138, SACHEN_8259B }, + { 139, SACHEN_8259C }, + { 140, JALECO_JF11 }, + { 141, SACHEN_8259A }, + { 142, KAISER_KS7032}, + { 143, SACHEN_TCA01 }, + { 144, AGCI_50282 }, + { 145, SACHEN_SA72007 }, + { 146, AVE_NINA06 }, // basically same as Mapper 79 (Nina006) + { 147, SACHEN_TCU01 }, + { 148, SACHEN_SA0037 }, + { 149, SACHEN_SA0036 }, + { 150, SACHEN_74LS374 }, + // 151 VS. system by Konami - Not going to be implemented (use MAME instead) + { 152, DIS_74X161X161X32 }, + { 153, BANDAI_LZ93 }, + { 154, NAMCOT_34X3 }, + { 155, STD_SXROM_A }, // diff compared to MMC1 concern WRAM + { 156, OPENCORP_DAOU306 }, + { 157, BANDAI_DATACH }, // Datach Reader games -> must go in the Datach subslot + { 158, TENGEN_800037 }, + { 159, BANDAI_LZ93EX1 }, // with 24c01 + { 160, SACHEN_SA009 }, + // 161 Unused + { 162, WAIXING_FS304}, // not confirmed, but a lot of chinese releases use it like this... + { 163, NANJING_BOARD}, + { 164, WAIXING_FFV }, + { 165, WAIXING_SH2 }, + { 166, SUBOR_TYPE1 }, + { 167, SUBOR_TYPE0 }, + { 168, UNL_RACERMATE }, + // 169 Unused + // 170 Fujiya + { 171, KAISER_KS7058 }, + { 172, TXC_DUMARACING }, + { 173, TXC_MJBLOCK }, + // 174 Unused + { 175, KAISER_KS7022}, + { 176, UNL_XIAOZY }, + { 177, HENGG_SRICH }, + { 178, WAIXING_SGZLZ }, + { 179, HENGG_XHZS }, + { 180, UXROM_CC }, + // 181 Unused + { 182, HOSENKAN_BOARD }, + { 183, BTL_SHUIGUAN }, + { 184, SUNSOFT_1 }, + { 185, STD_CNROM }, + { 186, FUKUTAKE_BOARD }, + { 187, UNL_KOF96 }, + { 188, BANDAI_KARAOKE }, + { 189, TXC_TW }, + // 190 Unused + { 191, WAIXING_TYPE_B }, + { 192, WAIXING_TYPE_C }, + { 193, NTDEC_FIGHTINGHERO }, + { 194, WAIXING_TYPE_D }, + { 195, WAIXING_TYPE_E }, + { 196, BTL_SBROS11 }, + { 197, UNL_SF3 }, + { 198, WAIXING_TYPE_F }, + { 199, WAIXING_TYPE_G }, + { 200, BMC_36IN1 }, + { 201, BMC_21IN1 }, + { 202, BMC_150IN1 }, + { 203, BMC_35IN1 }, + { 204, BMC_64IN1 }, + { 205, BMC_15IN1 }, + { 206, NAMCOT_34X3 }, + { 207, TAITO_X1_005 }, + { 208, GOUDER_37017 }, + { 209, JYCOMPANY_C }, + { 210, NAMCOT_175 }, + { 211, JYCOMPANY_B }, + { 212, BMC_SUPERHIK_300IN1 }, + { 213, BMC_NOVEL2 }, + { 214, BMC_SUPERGUN_20IN1 }, + { 215, SUPERGAME_BOOGERMAN }, + { 216, RCM_GS2015 }, + { 217, BMC_GOLDENCARD_6IN1 }, + { 218, NOCASH_NOCHR }, + // 219 UNL-A9746 (according to Cah4e3's code, no dump available (yet) + // 220 Unused + { 221, UNL_N625092 }, + { 222, BTL_DRAGONNINJA }, + { 223, WAIXING_TYPE_I }, // (according to NEStopia source, it's MMC3 with more WRAM) + { 224, WAIXING_TYPE_J }, // (according to NEStopia source, it's MMC3 with more WRAM) + { 225, BMC_72IN1 }, + { 226, BMC_76IN1 }, + { 227, BMC_1200IN1 }, + { 228, ACTENT_ACT52 }, + { 229, BMC_31IN1 }, + { 230, BMC_22GAMES }, + { 231, BMC_20IN1 }, + { 232, CAMERICA_BF9096 }, + { 233, BMC_SUPER22 }, + { 234, AVE_MAXI15 }, + { 235, BMC_GOLD150 }, // 235 Golden Game x-in-1 - Unsupported + // 236 Game 800-in-1 - Unsupported + // 237 Unused + { 238, UNL_603_5052 }, + // 239 Unused + { 240, CNE_SHLZ }, + { 241, TXC_COMMANDOS }, + { 242, WAIXING_WXZS }, + { 243, SACHEN_74LS374_ALT }, + { 244, CNE_DECATHLON }, + { 245, WAIXING_TYPE_H }, + { 246, CNE_FSB }, + // 247 Unused + // 248 Unused + { 249, WAIXING_SECURITY }, + { 250, NITRA_TDA }, + // 251 Shen Hua Jian Yun III?? - Unsupported + { 252, WAIXING_SGZ }, + // 253 Super 8-in-1 99 King Fighter?? - Unsupported + { 254, BTL_PIKACHUY2K }, + { 255, BMC_110IN1 }, +}; + +const nes_mmc *nes_mapper_lookup( int mapper ) +{ + int i; + + for (i = 0; i < ARRAY_LENGTH(mmc_list); i++) + { + if (mmc_list[i].iNesMapper == mapper) + return &mmc_list[i]; + } + + return NULL; +} + +#if 0 +int nes_get_mmc_id( running_machine &machine, int mapper ) +{ + const nes_mmc *mmc = nes_mapper_lookup(mapper); + + if (mmc == NULL) + fatalerror("Unimplemented Mapper %d\n", mapper); + + return mmc->pcb_id; +} +#endif + +/************************************************************* + + ines_mapr_setup + + setup the board specific pcb_id for a given mapper + + *************************************************************/ + +void ines_mapr_setup( int mapper, int *pcb_id ) +{ + const nes_mmc *mmc = nes_mapper_lookup(mapper); + if (mmc == NULL) + fatalerror("Unimplemented Mapper %d\n", mapper); + + *pcb_id = mmc->pcb_id; +} + +/************************************************************* + + call_load_ines + + *************************************************************/ + +void nes_cart_slot_device::call_load_ines() +{ + UINT32 vram_size = 0, prgram_size = 0, battery_size = 0, mapper_sram_size = 0; + UINT32 prg_size, vrom_size; + UINT8 header[0x10]; + UINT8 mapper, submapper = 0, local_options; + bool ines20 = FALSE, prg16k; + std::string mapinfo; + int pcb_id = 0, mapint1 = 0, mapint2 = 0, mapint3 = 0, mapint4 = 0; + int crc_hack = 0; + bool bus_conflict = FALSE; + + // read out the header + fseek(0, SEEK_SET); + fread(&header, 0x10); + + // SETUP step 1: getting PRG, VROM, VRAM sizes + prg16k = (header[4] == 1); + prg_size = prg16k ? 2 * 0x4000 : header[4] * 0x4000; + vrom_size = header[5] * 0x2000; + vram_size = 0x4000; + + // SETUP step 2: getting PCB and other settings + mapper = (header[6] & 0xf0) >> 4; + local_options = header[6] & 0x0f; + + switch (header[7] & 0xc) + { + case 0x4: + case 0xc: + // probably the header got corrupted: don't trust upper bits for mapper + break; + + case 0x8: // it's iNES 2.0 format + ines20 = TRUE; + case 0x0: + default: + mapper |= header[7] & 0xf0; + break; + } + + // use info from nes.hsi if available! + if (hashfile_extrainfo(*this, mapinfo)) + { + if (4 == sscanf(mapinfo.c_str(),"%d %d %d %d", &mapint1, &mapint2, &mapint3, &mapint4)) + { + /* image is present in nes.hsi: overwrite the header settings with these */ + mapper = mapint1; + local_options = mapint2 & 0x0f; + crc_hack = (mapint2 & 0xf0) >> 4; // this is used to differentiate among variants of the same Mapper (see below) + prg16k = (mapint3 == 1); + prg_size = prg16k ? 2 * 0x4000 : mapint3 * 0x4000; + vrom_size = mapint4 * 0x2000; + logerror("NES.HSI info: %d %d %d %d\n", mapint1, mapint2, mapint3, mapint4); + } + else + { + logerror("NES: [%s], Invalid mapinfo found\n", mapinfo.c_str()); + } + } + else + { + logerror("NES: No extrainfo found\n"); + } + + // use extended iNES2.0 info if available! + if (ines20) + { + mapper |= (header[8] & 0x0f) << 8; + // read submappers (based on 20140116 specs) + submapper = (header[8] & 0xf0 >> 8); + prg_size += ((header[9] & 0x0f) << 8) * 0x4000; + vrom_size += ((header[9] & 0xf0) << 4) * 0x2000; + } + ines_mapr_setup(mapper, &pcb_id); + + // handle submappers + if (submapper) + { + // 001: MMC1 + if (mapper == 1 && submapper == 3) + pcb_id = STD_SXROM_A; + else if (mapper == 1 && submapper == 5) + logerror("Unimplemented iNES2.0 submapper: SEROM/SHROM/SH1ROM.\n"); + // 002, 003, 007: UxROM, CNROM, AxROM + else if (mapper == 2 && submapper == 2) + bus_conflict = TRUE; + else if (mapper == 3 && submapper == 2) + bus_conflict = TRUE; + else if (mapper == 7 && submapper == 2) + bus_conflict = TRUE; + // 021, 023, 025: VRC4 / VRC2 + else if (mapper == 21 || mapper == 23 || mapper == 25) + { + // 021, 023, 025: VRC4 + int line_1 = submapper & 0x07; + int line_2 = (submapper & 0x08) ? line_1 + 1 : line_1 - 1; + if (line_2 >= 0 && line_2 <= 7) + { + pcb_id = KONAMI_VRC4; + m_cart->set_vrc_lines(line_1, line_2, 0); + } + else if (submapper == 15) + { + pcb_id = KONAMI_VRC2; + m_cart->set_vrc_lines(1, 0, 0); + } + } + // 032: Irem G101 + else if (mapper == 32 && submapper == 1) + { + m_cart->set_mirroring(PPU_MIRROR_HIGH); // Major League has hardwired mirroring + } + // iNES Mapper 034 + else if (mapper == 34 && submapper == 1) + { + pcb_id = AVE_NINA01; // Mapper 34 is used for 2 diff boards + } + // iNES Mapper 068 / Sunsoft 4 + else if (mapper == 68 && submapper == 1) + { + submapper = 0; + logerror("Unimplemented iNES2.0 submapper: SUNSOFT-DCS.\n"); + } + // iNES Mapper 071 + else if (mapper == 71 && submapper == 1) + { + m_cart->set_pcb_ctrl_mirror(TRUE); // Mapper 71 is used for 2 diff boards + } + // iNES Mapper 078 + else if (mapper == 78) + { + if (submapper == 1) + pcb_id = JALECO_JF16; // Mapper 78 is used for 2 diff boards + else if (submapper == 3) + pcb_id = IREM_HOLYDIVR; + } + // iNES Mapper 185 + else if (mapper == 185) + { + int ce_state = (submapper & 0x0c) >> 2; + m_cart->set_ce(0x03, ce_state); + } + // iNES Mapper 232 + else if (mapper == 210 && submapper == 1) + { + submapper = 0; + logerror("Unimplemented iNES2.0 submapper: CAMERICA-BF9096.\n"); + } + else if (submapper) + { + submapper = 0; + logerror("Undocumented iNES2.0 submapper, please report it to the MESS boards!\n"); + } + } + + // SETUP step 3: storing the info needed for emulation + m_pcb_id = pcb_id; + m_cart->set_mirroring(BIT(local_options, 0) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ); + if (BIT(local_options, 1)) + battery_size = NES_BATTERY_SIZE; // with original iNES format we can only support 8K WRAM battery + m_cart->set_trainer(BIT(local_options, 2) ? TRUE : FALSE); + m_cart->set_four_screen_vram(BIT(local_options, 3) ? TRUE : FALSE); + + if (ines20) + { + // PRGRAM/BWRAM (not fully supported, also due to lack of 2.0 files) + if ((header[10] & 0x0f) > 0) + prgram_size = 0x80 << ((header[10] & 0x0f) - 1); + if ((header[10] & 0xf0) > 0) + battery_size = 0x80 << (((header[10] & 0xf0) >> 4) - 1); + // VRAM + vram_size = 0; + if ((header[11] & 0x0f) > 0) + vram_size = 0x80 << ((header[11] & 0x0f) - 1); + if ((header[11] & 0xf0) > 0) + vram_size |= 0x80 << (((header[11] & 0xf0) >> 4) - 1); + // header[11] & 0xf0 is the size of battery backed VRAM, found so far in Racermate II only and not supported yet + } + else + { + // PRGRAM size is 8k for most games, but pirate carts often use different sizes, + // so its size has been added recently to the iNES format spec, but almost no image uses it + prgram_size = header[8] ? header[8] * 0x2000 : 0x2000; + } + + // a few mappers correspond to multiple PCBs, so we need a few additional checks and tweaks + switch (m_pcb_id) + { + case STD_NROM: + if (prg_size == 3 * 0x4000) // NROM368 are padded with 2k empty data at start to accomplish with iNES standard + { + m_pcb_id = STD_NROM368; + fseek(0x810, SEEK_SET); + prg_size = 0xb800; + } + break; + + case NOCASH_NOCHR: + // this mapper uses mirroring flags differently + m_cart->set_four_screen_vram(FALSE); + switch (local_options & 0x09) + { + case 0x00: + m_cart->set_mirroring(PPU_MIRROR_HORZ); + break; + case 0x01: + m_cart->set_mirroring(PPU_MIRROR_VERT); + break; + case 0x08: + m_cart->set_mirroring(PPU_MIRROR_LOW); + break; + case 0x09: + m_cart->set_mirroring(PPU_MIRROR_HIGH); + break; + } + break; + + case STD_CNROM: + if (mapper == 185 && !submapper) + { + switch (crc_hack) + { + case 0x0: // pin26: CE, pin27: CE (B-Wings, Bird Week) + m_cart->set_ce(0x03, 0x03); + break; + case 0x4: // pin26: CE, pin27: /CE (Mighty Bomb Jack, Spy Vs. Spy) + m_cart->set_ce(0x03, 0x01); + break; + case 0x8: // pin26: /CE, pin27: CE (Sansu 1, 2, 3 Nen, Othello) + m_cart->set_ce(0x03, 0x02); + break; + case 0xc: // pin26: /CE, pin27: /CE (Seicross v2.0) + m_cart->set_ce(0x03, 0x00); + break; + } + } + break; + + case KONAMI_VRC2: + if (mapper == 22) + m_cart->set_vrc_lines(0, 1, 1); + if (mapper == 23 && !crc_hack && !submapper) + m_cart->set_vrc_lines(1, 0, 0); + if (mapper == 23 && crc_hack && !submapper) + { + // here there are also Akumajou Special, Crisis Force, Parodius da!, Tiny Toons which are VRC-4 + m_cart->set_vrc_lines(3, 2, 0); + m_pcb_id = KONAMI_VRC4; // this allows for konami_irq to be installed at reset + } + break; + + case KONAMI_VRC4: + if (mapper == 21 && !submapper) // Wai Wai World 2 & Ganbare Goemon Gaiden 2 (the latter with crc_hack) + m_cart->set_vrc_lines(crc_hack ? 7 : 2, crc_hack ? 6 : 1, 0); + if (mapper == 25 && !submapper) // here there is also Ganbare Goemon Gaiden which is VRC-2 + m_cart->set_vrc_lines(crc_hack ? 2 : 0, crc_hack ? 3 : 1, 0); + break; + + case KONAMI_VRC6: + if (mapper == 24) + m_cart->set_vrc_lines(1, 0, 0); + if (mapper == 26) + m_cart->set_vrc_lines(0, 1, 0); + break; + + case IREM_G101: + if (crc_hack && !submapper) + m_cart->set_mirroring(PPU_MIRROR_HIGH); // Major League has hardwired mirroring + else if (!submapper) + m_cart->set_pcb_ctrl_mirror(TRUE); + break; + + case DIS_74X161X161X32: + if (mapper == 70) + m_cart->set_mirroring(PPU_MIRROR_VERT); // only hardwired mirroring makes different mappers 70 & 152 + else + m_cart->set_pcb_ctrl_mirror(TRUE); + break; + + case SUNSOFT_2: + if (mapper == 93) + m_cart->set_mirroring(PPU_MIRROR_VERT); // only hardwired mirroring makes different mappers 89 & 93 + else + m_cart->set_pcb_ctrl_mirror(TRUE); + break; + + case HES_BOARD: + if (crc_hack) + m_cart->set_pcb_ctrl_mirror(TRUE); // Mapper 113 is used for 2 diff boards + break; + + case CAMERICA_BF9093: + if (crc_hack && !submapper) + m_cart->set_pcb_ctrl_mirror(TRUE); // Mapper 71 is used for 2 diff boards + break; + + case STD_BXROM: + if (crc_hack && !submapper) + m_pcb_id = AVE_NINA01; // Mapper 34 is used for 2 diff boards + break; + + case BANDAI_LZ93: + if (crc_hack) + m_pcb_id = BANDAI_FJUMP2; // Mapper 153 is used for 2 diff boards + break; + + case IREM_HOLYDIVR: + if (crc_hack && !submapper) + m_pcb_id = JALECO_JF16; // Mapper 78 is used for 2 diff boards + break; + + case WAIXING_WXZS: + if (crc_hack) + m_pcb_id = WAIXING_DQ8; // Mapper 242 is used for 2 diff boards + break; + + case BMC_GOLD_7IN1: + if (crc_hack) + m_pcb_id = BMC_MARIOPARTY_7IN1; // Mapper 52 is used for 2 diff boards + break; + + case BTL_MARIOBABY: + if (crc_hack) + m_pcb_id = BTL_AISENSHINICOL; // Mapper 42 is used for 2 diff boards + break; + + case TAITO_X1_017: + mapper_sram_size = m_cart->get_mapper_sram_size(); + break; + + case TAITO_X1_005: + if (mapper == 207) + m_cart->set_x1_005_alt(TRUE); + mapper_sram_size = m_cart->get_mapper_sram_size(); + break; + + case NAMCOT_163: + mapper_sram_size = m_cart->get_mapper_sram_size(); + break; + //FIXME: we also have to fix Action 52 PRG loading somewhere... + + case BANDAI_DATACH: + fatalerror("Bandai Datach games have to be mounted in the Datach subslot!\n"); + break; + } + + // Finally turn off bus conflict emulation, because the pirate variants of the boards are bus conflict free and games would glitch + m_cart->set_bus_conflict(bus_conflict); + + // SETUP step 4: logging what we have found + if (!ines20) + { + logerror("Loaded game in iNES format:\n"); + logerror("-- Mapper %d\n", mapper); + logerror("-- PRG 0x%x (%d x 16k chunks)\n", prg_size, prg_size / 0x4000); + logerror("-- VROM 0x%x (%d x 8k chunks)\n", vrom_size, vrom_size / 0x2000); + logerror("-- VRAM 0x%x (%d x 8k chunks)\n", vram_size, vram_size / 0x2000); + logerror("-- Mirroring %s\n", BIT(header[6], 0) ? "Vertical" : "Horizontal"); + if (battery_size) + logerror("-- Battery found\n"); + if (m_cart->get_trainer()) + logerror("-- Trainer found\n"); + if (m_cart->get_four_screen_vram()) + logerror("-- 4-screen VRAM\n"); + logerror("-- TV System: %s\n", ((header[10] & 3) == 0) ? "NTSC" : (header[10] & 1) ? "Both NTSC and PAL" : "PAL"); + } + else + { + logerror("Loaded game in Extended iNES format:\n"); + logerror("-- Mapper: %d\n", mapper); + logerror("-- Submapper: %d\n", (header[8] & 0xf0) >> 4); + logerror("-- PRG 0x%x (%d x 16k chunks)\n", prg_size, prg_size / 0x4000); + logerror("-- VROM 0x%x (%d x 8k chunks)\n", vrom_size, vrom_size / 0x2000); + logerror("-- VRAM 0x%x (%d x 8k chunks)\n", vram_size, vram_size / 0x2000); + logerror("-- PRG NVWRAM: %d\n", (header[10] & 0xf0) >> 4); + logerror("-- PRG WRAM: %d\n", header[10] & 0x0f); + logerror("-- CHR NVWRAM: %d\n", (header[11] & 0xf0) >> 4); + logerror("-- CHR WRAM: %d\n", header[11] & 0x0f); + logerror("-- TV System: %s\n", (header[12] & 2) ? "Both NTSC and PAL" : (header[12] & 1) ? "PAL" : "NTSC"); + } + + // SETUP step 5: allocate pointers for PRG/VROM + if (prg_size) + m_cart->prg_alloc(prg_size, tag()); + if (vrom_size) + m_cart->vrom_alloc(vrom_size, tag()); + + // if there is a trainer, skip it for the moment + if (m_cart->get_trainer()) + fseek(0x210, SEEK_SET); + + // SETUP step 6: at last load the data! + // Read in the program chunks + if (prg16k) + { + fread(m_cart->get_prg_base(), 0x4000); + memcpy(m_cart->get_prg_base() + 0x4000, m_cart->get_prg_base(), 0x4000); + } + else + fread(m_cart->get_prg_base(), m_cart->get_prg_size()); +#if SPLIT_PRG + { + FILE *prgout; + char outname[255]; + + sprintf(outname, "%s.prg", filename()); + prgout = fopen(outname, "wb"); + if (prgout) + { + fwrite(m_cart->get_prg_base(), 1, 0x4000 * m_cart->get_prg_size(), prgout); + osd_printf_error("Created PRG chunk\n"); + } + + fclose(prgout); + } +#endif + + // Read in any chr chunks + if (m_cart->get_vrom_size()) + fread(m_cart->get_vrom_base(), m_cart->get_vrom_size()); + +#if SPLIT_CHR + if (state->m_chr_chunks > 0) + { + FILE *chrout; + char outname[255]; + + sprintf(outname, "%s.chr", filename()); + chrout= fopen(outname, "wb"); + if (chrout) + { + fwrite(m_cart->get_vrom_base(), 1, m_cart->get_vrom_size(), chrout); + osd_printf_error("Created CHR chunk\n"); + } + fclose(chrout); + } +#endif + + // SETUP steps 7: allocate the remaining pointer, when needed + if (vram_size) + m_cart->vram_alloc(vram_size); + if (prgram_size || m_cart->get_trainer()) + { + if (prgram_size) + m_cart->prgram_alloc(prgram_size); + else + m_cart->prgram_alloc(0x2000); + if (m_cart->get_trainer()) + { + fseek(0x10, SEEK_SET); + fread(m_cart->get_prgram_base() + 0x1000, 0x200); + } + } + + + // Attempt to load a battery file for this ROM + // A few boards have internal RAM with a battery (MMC6, Taito X1-005 & X1-017, etc.) + if (battery_size || mapper_sram_size) + { + UINT32 tot_size = battery_size + mapper_sram_size; + dynamic_buffer temp_nvram(tot_size); + battery_load(&temp_nvram[0], tot_size, 0x00); + if (battery_size) + { + //printf("here %d\n", battery_size); + m_cart->battery_alloc(battery_size); + memcpy(m_cart->get_battery_base(), &temp_nvram[0], battery_size); + } + if (mapper_sram_size) + memcpy(m_cart->get_mapper_sram_base(), &temp_nvram[battery_size], m_cart->get_mapper_sram_size()); + } +} + +const char * nes_cart_slot_device::get_default_card_ines(UINT8 *ROM, UINT32 len) +{ + UINT8 mapper, submapper = 0; + bool ines20 = FALSE; + std::string mapinfo; + int pcb_id = 0, mapint1 = 0, mapint2 = 0, mapint3 = 0, mapint4 = 0; + int crc_hack = 0; + + mapper = (ROM[6] & 0xf0) >> 4; + + switch (ROM[7] & 0xc) + { + case 0x4: + case 0xc: + // probably the header got corrupted: don't trust upper bits for mapper + break; + + case 0x8: // it's iNES 2.0 format + ines20 = TRUE; + case 0x0: + default: + mapper |= ROM[7] & 0xf0; + break; + } + + // use info from nes.hsi if available! + if (hashfile_extrainfo(*this, mapinfo)) + { + if (4 == sscanf(mapinfo.c_str(),"%d %d %d %d", &mapint1, &mapint2, &mapint3, &mapint4)) + { + /* image is present in nes.hsi: overwrite the header settings with these */ + mapper = mapint1; + crc_hack = (mapint2 & 0xf0) >> 4; // this is used to differentiate among variants of the same Mapper (see below) + } + } + + // use extended iNES2.0 info if available! + if (ines20) + { + mapper |= (ROM[8] & 0x0f) << 8; + // read submappers (based on 20140116 specs) + submapper = (ROM[8] & 0xf0 >> 8); + } + + ines_mapr_setup(mapper, &pcb_id); + + // handle submappers + if (submapper) + { + // 001: MMC1 + if (mapper == 1 && submapper == 3) + pcb_id = STD_SXROM_A; + else if (mapper == 1 && submapper == 5) + logerror("Unimplemented iNES2.0 submapper: SEROM/SHROM/SH1ROM.\n"); + // 021, 023, 025: VRC4 / VRC2 + else if (mapper == 21 || mapper == 23 || mapper == 25) + { + // 021, 023, 025: VRC4 + int line_1 = submapper & 0x07; + int line_2 = (submapper & 0x08) ? line_1 + 1 : line_1 - 1; + if (line_2 >= 0 && line_2 <= 7) + pcb_id = KONAMI_VRC4; + else if (submapper == 15) + pcb_id = KONAMI_VRC2; + } + // iNES Mapper 034 + else if (mapper == 34 && submapper == 1) + { + pcb_id = AVE_NINA01; // Mapper 34 is used for 2 diff boards + } + // iNES Mapper 078 + else if (mapper == 78) + { + if (submapper == 1) + pcb_id = JALECO_JF16; // Mapper 78 is used for 2 diff boards + else if (submapper == 3) + pcb_id = IREM_HOLYDIVR; + } + } + + // solve mapper conflicts + switch (pcb_id) + { + case STD_NROM: + if (ROM[4] == 3) + pcb_id = STD_NROM368; + break; + + case KONAMI_VRC2: + if (mapper == 23 && crc_hack && !submapper) + pcb_id = KONAMI_VRC4; // this allows for konami_irq to be installed at reset + break; + + case STD_BXROM: + if (crc_hack && !submapper) + pcb_id = AVE_NINA01; // Mapper 34 is used for 2 diff boards + break; + + case BANDAI_LZ93: + if (crc_hack) + pcb_id = BANDAI_FJUMP2; // Mapper 153 is used for 2 diff boards + break; + + case IREM_HOLYDIVR: + if (crc_hack && !submapper) + pcb_id = JALECO_JF16; // Mapper 78 is used for 2 diff boards + break; + + case WAIXING_WXZS: + if (crc_hack) + pcb_id = WAIXING_DQ8; // Mapper 242 is used for 2 diff boards + break; + + case BMC_GOLD_7IN1: + if (crc_hack) + pcb_id = BMC_MARIOPARTY_7IN1; // Mapper 52 is used for 2 diff boards + break; + + case BTL_MARIOBABY: + if (crc_hack) + pcb_id = BTL_AISENSHINICOL; // Mapper 42 is used for 2 diff boards + break; + } + + return nes_get_slot(pcb_id); +} diff --git a/src/devices/bus/nes/nes_pcb.inc b/src/devices/bus/nes/nes_pcb.inc new file mode 100644 index 00000000000..57cdf3d498c --- /dev/null +++ b/src/devices/bus/nes/nes_pcb.inc @@ -0,0 +1,609 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/***************************************************************************************** + + NES Cart PCBs Emulation + +****************************************************************************************/ + +struct nes_pcb +{ + const char *slot_opt; + int pcb_id; +}; + +// Here, we take the feature attribute from .xml (i.e. the PCB name) and we assign a unique ID to it +static const nes_pcb pcb_list[] = +{ + { "nrom", STD_NROM }, + { "hvc_basic", HVC_FAMBASIC }, + { "nrom368", STD_NROM368 }, + { "nrom_gg", GG_NROM }, + { "uxrom", STD_UXROM }, + { "unrom_cc", UXROM_CC }, + { "cnrom", STD_CNROM }, + { "bandai_pt554", BANDAI_PT554 }, + { "cprom", STD_CPROM }, + { "axrom", STD_AXROM }, + { "pxrom", STD_PXROM }, + { "fxrom", STD_FXROM }, + { "bnrom", STD_BXROM }, + { "gxrom", STD_GXROM }, + { "un1rom", STD_UN1ROM }, + { "sxrom", STD_SXROM }, + { "sorom", STD_SOROM }, + { "sxrom_a", STD_SXROM_A }, + { "sorom_a", STD_SOROM_A }, + { "txrom", STD_TXROM }, + { "hkrom", STD_HKROM }, + { "tqrom", STD_TQROM }, + { "txsrom", STD_TXSROM }, + { "exrom", STD_EXROM }, + { "disksys", STD_DISKSYS }, + { "pal_zz", PAL_ZZ }, + { "nes_qj", NES_QJ }, + { "nes_event", STD_EVENT }, + { "discrete_74x139", DIS_74X139X74 }, + { "discrete_74x377", DIS_74X377 }, + { "discrete_74x161", DIS_74X161X161X32 }, + { "bitcorp_dis", DIS_74X161X138 }, + { "lz93d50", BANDAI_LZ93 }, + { "lz93d50_ep1", BANDAI_LZ93EX1 }, + { "lz93d50_ep2", BANDAI_LZ93EX2 }, + { "fcg", BANDAI_FCG }, + { "fjump2", BANDAI_FJUMP2 }, + { "datach", BANDAI_DATACH }, + { "karastudio", BANDAI_KARAOKE }, + { "oekakids", BANDAI_OEKAKIDS }, + { "g101", IREM_G101 }, + { "lrog017", IREM_LROG017 }, + { "h3001", IREM_H3001 }, + { "holydivr", IREM_HOLYDIVR }, + { "tam_s1", IREM_TAM_S1 }, + { "jf11", JALECO_JF11 }, + { "jf13", JALECO_JF13 }, + { "jf16", JALECO_JF16 }, + { "jf17", JALECO_JF17 }, + { "jf17pcm", JALECO_JF17_ADPCM }, + { "jf19", JALECO_JF19 }, + { "jf19pcm", JALECO_JF19_ADPCM }, + { "ss88006", JALECO_SS88006 }, + { "jf23", JALECO_JF23 }, + { "jf24", JALECO_JF24 }, + { "jf29", JALECO_JF29 }, + { "jf33", JALECO_JF33 }, + { "vrc1", KONAMI_VRC1 }, + { "vrc2", KONAMI_VRC2 }, + { "vrc3", KONAMI_VRC3 }, + { "vrc4", KONAMI_VRC4 }, + { "vrc6", KONAMI_VRC6 }, + { "vrc7", KONAMI_VRC7 }, + { "namcot_163", NAMCOT_163 }, + { "namcot_175", NAMCOT_175 }, + { "namcot_340", NAMCOT_340 }, + { "namcot_3433", NAMCOT_34X3 }, // DxROM is a Nintendo board for US versions of the 3433/3443 games + { "namcot_3425", NAMCOT_3425 }, + { "namcot_3446", NAMCOT_3446 }, + { "sunsoft1", SUNSOFT_1 }, + { "sunsoft2", SUNSOFT_2 }, + { "sunsoft3", SUNSOFT_3 }, + { "sunsoft4", SUNSOFT_4 }, + { "sunsoft_dcs", SUNSOFT_DCS }, + { "sunsoft_fme7", SUNSOFT_FME7 }, // JxROM is a Nintendo board for US versions of the Sunsoft FME7 games + { "sunsoft5a", SUNSOFT_FME7 }, + { "sunsoft5b", SUNSOFT_5 }, + { "tc0190fmc", TAITO_TC0190FMC }, + { "tc0190fmcp", TAITO_TC0190FMCP }, + { "tc0350fmr", TAITO_TC0190FMC }, + { "x1_005", TAITO_X1_005 }, // two variants exist, depending on pin17 & pin31 connections + { "x1_017", TAITO_X1_017 }, + { "nina001", AVE_NINA01 }, + { "nina006", AVE_NINA06 }, + { "maxi15", AVE_MAXI15 }, + { "bf9093", CAMERICA_BF9093 }, + { "bf9096", CAMERICA_BF9096 }, + { "goldenfive", CAMERICA_GOLDENFIVE }, + { "ade" , CAMERICA_ALADDIN }, + { "cne_decathl", CNE_DECATHLON }, + { "cne_fsb", CNE_FSB }, + { "cne_shlz", CNE_SHLZ }, + { "nanjing", NANJING_BOARD }, // mapper 163 + { "ntdec_asder", NTDEC_ASDER }, // mapper 112 + { "ntdec_fh", NTDEC_FIGHTINGHERO }, // mapper 193 + { "sa009", SACHEN_SA009 }, + { "sa0036", SACHEN_SA0036 }, + { "sa0037", SACHEN_SA0037 }, + { "sa72007", SACHEN_SA72007 }, + { "sa72008", SACHEN_SA72008 }, + { "tca01", SACHEN_TCA01 }, + { "s8259a", SACHEN_8259A }, + { "s8259b", SACHEN_8259B }, + { "s8259c", SACHEN_8259C }, + { "s8259d", SACHEN_8259D }, + { "s74x374", SACHEN_74LS374 }, + { "s74x374a", SACHEN_74LS374_ALT }, /* FIXME: Made up boards some different handling */ + { "tcu01", SACHEN_TCU01 }, + { "tcu02", SACHEN_TCU02 }, + { "sa9602b", SACHEN_SA9602B }, + { "tengen_800008", TENGEN_800008 }, /* FIXME: Is this the same as mapper 3? */ + { "tengen_800032", TENGEN_800032 }, + { "tengen_800037", TENGEN_800037 }, + { "txc_22211", TXC_22211 }, + { "txc_dumarc", TXC_DUMARACING }, + { "txc_mjblock", TXC_MJBLOCK }, + { "txc_strikew", TXC_STRIKEW }, + { "txc_commandos", TXC_COMMANDOS }, + { "waixing_a", WAIXING_TYPE_A }, + { "waixing_a1", WAIXING_TYPE_A1 }, /* FIXME: Made up boards the different CHRRAM banks (see Ji Jia Zhan Shi }, */ + { "waixing_b", WAIXING_TYPE_B }, + { "waixing_c", WAIXING_TYPE_C }, + { "waixing_d", WAIXING_TYPE_D }, + { "waixing_e", WAIXING_TYPE_E }, + { "waixing_f", WAIXING_TYPE_F }, + { "waixing_g", WAIXING_TYPE_G }, + { "waixing_h", WAIXING_TYPE_H }, + { "waixing_h1", WAIXING_TYPE_H1 }, + { "waixing_i", WAIXING_TYPE_I }, + { "waixing_j", WAIXING_TYPE_J }, + { "waixing_sgz", WAIXING_SGZ }, + { "waixing_sgzlz", WAIXING_SGZLZ }, + { "waixing_sec", WAIXING_SECURITY }, + { "waixing_ffv", WAIXING_FFV }, + { "waixing_wxzs", WAIXING_WXZS }, + { "waixing_wxzs2", WAIXING_WXZS2 }, + { "waixing_dq8", WAIXING_DQ8 }, + { "waixing_sh2", WAIXING_SH2 }, + { "fs304", WAIXING_FS304 }, // used in Zelda 3 by Waixing + { "cony", CONY_BOARD }, + { "yoko", YOKO_BOARD }, + { "hengg_srich", HENGG_SRICH }, + { "hengg_xhzs", HENGG_XHZS }, + { "hengg_shjy3", HENGG_SHJY3 }, // mapper 253 + { "hes", HES_BOARD }, + { "hosenkan", HOSENKAN_BOARD }, + { "ks7058", KAISER_KS7058 }, + { "ks202", KAISER_KS202 }, // mapper 56 + { "ks7022", KAISER_KS7022 }, // mapper 175 + { "ks7017", KAISER_KS7017 }, + { "ks7032", KAISER_KS7032 }, // mapper 142 + { "ks7031", KAISER_KS7031 }, // used in Dracula II (FDS Conversion) + { "ks7012", KAISER_KS7012 }, // used in Zanac (FDS Conversion) + { "ks7013b", KAISER_KS7013B }, // used in Highway Star (FDS Conversion) + { "gs2015", RCM_GS2015 }, + { "gs2004", RCM_GS2004 }, + { "gs2013", RCM_GS2013 }, + { "tf9in1", RCM_TF9IN1 }, + { "3dblock", RCM_3DBLOCK }, // NROM + IRQ? + { "racermate", UNL_RACERMATE }, // mapper 168 + { "agci_50282", AGCI_50282 }, + { "dreamtech01", DREAMTECH_BOARD }, + { "fukutake", FUKUTAKE_BOARD }, + { "futuremedia", FUTUREMEDIA_BOARD }, + { "magicseries", MAGICSERIES_MD }, + { "daou_306", OPENCORP_DAOU306 }, + { "subor0", SUBOR_TYPE0 }, + { "subor1", SUBOR_TYPE1 }, + { "cc21", UNL_CC21 }, + { "xiaozy", UNL_XIAOZY }, + { "edu2k", UNL_EDU2K }, + { "t230", UNL_T230 }, + { "mk2", UNL_MK2 }, + // misc bootleg boards + { "ax5705", UNL_AX5705 }, + { "sc127", UNL_SC127 }, + { "mariobaby", BTL_MARIOBABY }, + { "asnicol", BTL_AISENSHINICOL }, + { "smb3pirate", BTL_SMB3 }, + { "btl_dninja", BTL_DRAGONNINJA }, + { "whirl2706", WHIRLWIND_2706 }, + { "smb2j", UNL_SMB2J }, + { "smb2ja", BTL_SMB2JA }, + { "smb2jb", BTL_SMB2JB }, + { "09034a", BTL_09034A }, + { "tobidase", BTL_TOBIDASE }, // mapper 120 + { "dbz5", REXSOFT_DBZ5 }, + { "sl1632", REXSOFT_SL1632 }, + { "somari", SOMARI_SL12 }, // mapper 116 + { "nitra", NITRA_TDA }, + { "ks7057", UNL_KS7057 }, // mapper 196 alt (for Street Fighter VI / Fight Street VI }, + { "sbros11", BTL_SBROS11 }, + { "family4646", BMC_FAMILY_4646 }, + { "pikay2k", BTL_PIKACHUY2K }, // mapper 254 + { "8237", UNL_8237 }, + { "sg_lionk", SUPERGAME_LIONKING }, + { "sg_boog", SUPERGAME_BOOGERMAN }, + { "kasing", KASING_BOARD }, + { "kay", KAY_BOARD }, + { "h2288", UNL_H2288 }, + { "unl_6035052", UNL_603_5052 }, // mapper 238? + { "txc_tw", TXC_TW }, + { "kof97", UNL_KOF97 }, + { "kof96", UNL_KOF96 }, + { "sfight3", UNL_SF3 }, + { "gouder", GOUDER_37017 }, + { "benshieng", BMC_BENSHIENG }, + { "action52", ACTENT_ACT52 }, + { "caltron6in1", CALTRON_6IN1 }, + { "rumblestation", RUMBLESTATION_BOARD }, // mapper 46 + { "svision16", SVISION16_BOARD }, + { "n625092", UNL_N625092 }, + { "a65as", BMC_A65AS }, + { "t262", BMC_T262 }, + { "novel1", BMC_NOVEL1 }, + { "novel2", BMC_NOVEL2 }, // mapper 213... same as BMC-NOVELDIAMOND9999999IN1 board? + { "studyngame", UNL_STUDYNGAME }, // mapper 39 + { "sgun20in1", BMC_SUPERGUN_20IN1 }, + { "bmc_vt5201", BMC_VT5201 }, // mapper 60 otherwise + { "bmc_d1038", BMC_VT5201 }, // mapper 60? + { "810544c", BMC_810544 }, + { "ntd03", BMC_NTD_03 }, + { "bmc_gb63", BMC_G63IN1 }, + { "bmc_gka", BMC_GKA }, + { "bmc_gkb", BMC_GKB }, + { "bmc_ws", BMC_WS }, + { "bmc_hik300", BMC_SUPERHIK_300IN1 }, + { "bmc_s700", BMC_SUPER_700IN1 }, + { "bmc_ball11", BMC_BALLGAMES_11IN1 }, + { "bmc_22games", BMC_22GAMES }, + { "bmc_64y2k", BMC_64IN1NR }, + { "bmc_12in1", BMC_12IN1 }, + { "bmc_20in1", BMC_20IN1 }, + { "bmc_21in1", BMC_21IN1 }, + { "bmc_31in1", BMC_31IN1 }, + { "bmc_35in1", BMC_35IN1 }, + { "bmc_36in1", BMC_36IN1 }, + { "bmc_64in1", BMC_64IN1 }, + { "bmc_70in1", BMC_70IN1 }, + { "bmc_72in1", BMC_72IN1 }, + { "bmc_76in1", BMC_76IN1 }, + { "bmc_s42in1", BMC_76IN1 }, + { "bmc_110in1", BMC_110IN1 }, + { "bmc_150in1", BMC_150IN1 }, + { "bmc_190in1", BMC_190IN1 }, + { "bmc_800in1", BMC_800IN1 }, + { "bmc_1200in1", BMC_1200IN1 }, + { "bmc_8157", BMC_8157 }, + { "bmc_g146", BMC_G146 }, + { "bmc_11160", BMC_11160 }, + { "fk23c", BMC_FK23C }, + { "fk23ca", BMC_FK23CA }, + { "s24in1c03", BMC_S24IN1SC03 }, + { "bmc_15in1", BMC_15IN1 }, + { "bmc_sbig7in1", BMC_SUPERBIG_7IN1 }, + { "bmc_hik8in1", BMC_HIK8IN1 }, + { "bmc_hik4in1", BMC_SUPERHIK_4IN1 }, + { "bmc_mario7in1", BMC_MARIOPARTY_7IN1 }, + { "bmc_gold7in1", BMC_GOLD_7IN1 }, + { "bmc_gc6in1", BMC_GOLDENCARD_6IN1 }, + { "bmc_411120c", BMC_411120C }, + { "bmc_830118c", BMC_830118C }, + { "pjoy84", BMC_PJOY84 }, + { "bmc_gold150", BMC_GOLD150 }, + { "bmc_gold260", BMC_GOLD260 }, + { "bmc_power255", BMC_CH001 }, + { "bmc_s22games", BMC_SUPER22 }, + { "bmc_reset4", BMC_4IN1RESET }, + { "bmc_reset42", BMC_42IN1RESET }, + { "jyc_a", JYCOMPANY_A }, + { "jyc_b", JYCOMPANY_B }, + { "jyc_c", JYCOMPANY_C }, + { "tek90", JYCOMPANY_A }, + { "sa9602b", SACHEN_SA9602B }, + { "unl_shero", SACHEN_SHERO }, + { "mmalee2", UNL_MMALEE }, + { "unl_2708", UNL_2708 }, + { "unl_lh10", UNL_LH10 }, + { "unl_lh32", UNL_LH32 }, + { "unl_lh53", UNL_LH53 }, + { "unl_ac08", UNL_AC08 }, + { "unl_bb", UNL_BB }, + { "unl_malisb", UNL_MALISB }, + { "sgpipe", BTL_SHUIGUAN }, + { "unl_whero", UNL_WORLDHERO }, + { "unl_43272", UNL_43272 }, + { "tf1201", UNL_TF1201 }, + { "unl_cfight", UNL_CITYFIGHT }, + { "nocash_nochr", NOCASH_NOCHR }, + { "nes_action53", BTL_ACTION53 }, + { "nes_2a03pur", BTL_2A03_PURITANS }, + { "ffe3", FFE3_BOARD }, + { "ffe4", FFE4_BOARD }, + { "ffe8", FFE8_BOARD }, + { "8237a", UNSUPPORTED_BOARD }, + { "ninjaryu", UNSUPPORTED_BOARD }, + { "unl_dance", UNSUPPORTED_BOARD }, + { "bmc_hik_kof", UNSUPPORTED_BOARD }, + { "onebus", UNSUPPORTED_BOARD }, + { "coolboy", UNSUPPORTED_BOARD }, + { "btl_900218", UNSUPPORTED_BOARD }, // pirate The Lord of King, to be emulated soon + { "a9746", UNSUPPORTED_BOARD }, + { "dance2k", UNSUPPORTED_BOARD }, + { "pec586", UNSUPPORTED_BOARD }, + { "test", TEST_BOARD }, + { "unknown", UNKNOWN_BOARD } // a few pirate dumps uses the wrong mapper... +}; + +static const nes_pcb *nes_pcb_lookup( const char *slot ) +{ + for (int i = 0; i < ARRAY_LENGTH(pcb_list); i++) + { + if (!core_stricmp(pcb_list[i].slot_opt, slot)) + return &pcb_list[i]; + } + return NULL; +} + +static const nes_pcb *nes_id_lookup( int id ) +{ + for (int i = 0; i < ARRAY_LENGTH(pcb_list); i++) + { + if (pcb_list[i].pcb_id == id) + return &pcb_list[i]; + } + return NULL; +} + +int nes_cart_slot_device::nes_get_pcb_id( const char *slot ) +{ + const nes_pcb *pcb = nes_pcb_lookup(slot); + + if (pcb == NULL) + fatalerror("Unimplemented PCB type %s\n", slot); + + return pcb->pcb_id; +} + + +const char * nes_cart_slot_device::nes_get_slot( int pcb_id ) +{ + const nes_pcb *pcb = nes_id_lookup(pcb_id); + + if (pcb == NULL) + fatalerror("Unimplemented PCB ID %d\n", pcb_id); + + return pcb->slot_opt; +} + + +/************************************************************* + + nes_pcb_reset + + Resets the mmc bankswitch areas to their defaults. + It returns a value "err" that indicates if it was + successful. Possible values for err are: + + 0 = success + 1 = no pcb found + 2 = pcb not supported + + *************************************************************/ + +struct nes_cart_lines +{ + const char *tag; + int line; +}; + +static const struct nes_cart_lines nes_cart_lines_table[] = +{ + { "PRG A0", 0 }, + { "PRG A1", 1 }, + { "PRG A2", 2 }, + { "PRG A3", 3 }, + { "PRG A4", 4 }, + { "PRG A5", 5 }, + { "PRG A6", 6 }, + { "PRG A7", 7 }, + { "CHR A10", 10 }, + { "CHR A11", 11 }, + { "CHR A12", 12 }, + { "CHR A13", 13 }, + { "CHR A14", 14 }, + { "CHR A15", 15 }, + { "CHR A16", 16 }, + { "CHR A17", 17 }, + { "NC", 127 }, + { 0 } +}; + +static int nes_cart_get_line( const char *feature ) +{ + const struct nes_cart_lines *nes_line = &nes_cart_lines_table[0]; + + if (feature == NULL) + return 128; + + while (nes_line->tag) + { + if (strcmp(nes_line->tag, feature) == 0) + break; + + nes_line++; + } + + return nes_line->line; +} + +void nes_cart_slot_device::call_load_pcb() +{ + UINT32 vram_size = 0, prgram_size = 0, battery_size = 0, mapper_sram_size = 0; + // SETUP step 1: getting PRG, VROM, VRAM sizes + UINT32 prg_size = get_software_region_length("prg"); + UINT32 vrom_size = get_software_region_length("chr"); + vram_size = get_software_region_length("vram"); + vram_size += get_software_region_length("vram2"); + + // validate the xml fields + if (!prg_size) + fatalerror("No PRG entry for this software! Please check if the xml list got corrupted\n"); + if (prg_size < 0x8000) + fatalerror("PRG entry is too small! Please check if the xml list got corrupted\n"); + + // SETUP step 2: getting PCB and other settings + if (get_feature("slot")) + m_pcb_id = nes_get_pcb_id(get_feature("slot")); + else + m_pcb_id = NO_BOARD; + + // SETUP step 3: storing the info needed for emulation + if (get_software_region("bwram") != NULL) + battery_size = get_software_region_length("bwram"); + + if (get_software_region("wram") != NULL) + prgram_size = get_software_region_length("wram"); + + if (get_feature("mirroring")) + { + const char *mirroring = get_feature("mirroring"); + if (!strcmp(mirroring, "horizontal")) + m_cart->set_mirroring(PPU_MIRROR_HORZ); + if (!strcmp(mirroring, "vertical")) + m_cart->set_mirroring(PPU_MIRROR_VERT); + if (!strcmp(mirroring, "high")) + m_cart->set_mirroring(PPU_MIRROR_HIGH); + if (!strcmp(mirroring, "low")) + m_cart->set_mirroring(PPU_MIRROR_LOW); + if (!strcmp(mirroring, "4screen")) + { + // A few boards uses 4-screen mirroring: Gauntlet (DDROM or TRR1ROM or Tengen 800004), + // Rad Racer II (TVROM), and Napoleon Senki (IREM LROG017 with 74*161/161/21/138) + m_cart->set_four_screen_vram(TRUE); + m_cart->set_mirroring(PPU_MIRROR_4SCREEN); + } + if (!strcmp(mirroring, "pcb_controlled")) + { + // A few boards have variants with hardcoded mirroring and variants with mapper + // controlled mirroring. We use a variable to avoid the need of dupe devices. + // See e.g. HES 6-in-1 vs other HES games, Irem Major League vs other G-101 games, + // Sunsoft-2 Shanghai vs Mito Koumon, Camerica BF9093 games vs BF9097 games, etc. + // Boards where all games control mirroring do not make real use of this. + m_cart->set_pcb_ctrl_mirror(TRUE); + } + } + + /* Check for pins in specific boards which require them */ + if (m_pcb_id == STD_CNROM) + { + int mask = 0, state = 0; + + if (get_feature("chr-pin26") != NULL) + { + mask |= 0x01; + state |= !strcmp(get_feature("chr-pin26"), "CE") ? 0x01 : 0; + } + if (get_feature("chr-pin27") != NULL) + { + mask |= 0x02; + state |= !strcmp(get_feature("chr-pin27"), "CE") ? 0x02 : 0; + } + + m_cart->set_ce(mask, state); + } + + if (m_pcb_id == TAITO_X1_005 && get_feature("x1-pin17") != NULL && get_feature("x1-pin31") != NULL) + { + if (!strcmp(get_feature("x1-pin17"), "CIRAM A10") && !strcmp(get_feature("x1-pin31"), "NC")) + m_cart->set_x1_005_alt(TRUE); + } + + if (m_pcb_id == KONAMI_VRC2) + { + m_cart->set_vrc_lines(nes_cart_get_line(get_feature("vrc2-pin3")), + nes_cart_get_line(get_feature("vrc2-pin4")), + (nes_cart_get_line(get_feature("vrc2-pin21")) != 10) ? 1 : 0); +// osd_printf_error("VRC-2, pin3: A%d, pin4: A%d, pin21: %d\n", nes_cart_get_line(get_feature("vrc2-pin3")), nes_cart_get_line(get_feature("vrc2-pin4")), +// nes_cart_get_line(get_feature("vrc2-pin21"))); + } + + if (m_pcb_id == KONAMI_VRC4) + { + m_cart->set_vrc_lines(nes_cart_get_line(get_feature("vrc4-pin3")), + nes_cart_get_line(get_feature("vrc4-pin4")), + 0); +// osd_printf_error("VRC-4, pin3: A%d, pin4: A%d\n", nes_cart_get_line(get_feature("vrc4-pin3"), nes_cart_get_line(get_feature("vrc4-pin4")); + } + + if (m_pcb_id == KONAMI_VRC6) + { + m_cart->set_vrc_lines(nes_cart_get_line(get_feature("vrc6-pin9")), + nes_cart_get_line(get_feature("vrc6-pin10")), + 0); +// osd_printf_error("VRC-6, pin9: A%d, pin10: A%d\n", nes_cart_get_line(get_feature("vrc6-pin9"), nes_cart_get_line(get_feature("vrc6-pin10")); + } + + if (m_pcb_id == STD_HKROM || m_pcb_id == TAITO_X1_017) + mapper_sram_size = m_cart->get_mapper_sram_size(); + + if (m_pcb_id == TAITO_X1_005 || m_pcb_id == NAMCOT_163) + { + if (get_feature("batt")) + mapper_sram_size = m_cart->get_mapper_sram_size(); + } + + + // pirate variants of boards with bus conflict are often not suffering from it + // and actually games glitch if bus conflict is emulated... + if (get_feature("bus_conflict") && !strcmp(get_feature("bus_conflict"), "no")) + m_cart->set_bus_conflict(FALSE); + + + // SETUP step 4: logging what we have found + logerror("Loaded game from softlist:\n"); + logerror("-- PCB: %s", get_feature("pcb")); + if (m_pcb_id == UNSUPPORTED_BOARD) + logerror(" (currently not supported by MESS)"); + logerror("\n-- PRG 0x%x (%d x 16k chunks)\n", prg_size, prg_size / 0x4000); + logerror("-- VROM 0x%x (%d x 8k chunks)\n", vrom_size, vrom_size / 0x2000); + logerror("-- VRAM 0x%x (%d x 8k chunks)\n", vram_size, vram_size / 0x2000); + logerror("-- PRG NVWRAM: %d\n", battery_size + mapper_sram_size); + logerror("-- PRG WRAM: %d\n", prgram_size); + + // SETUP steps 5/6: allocate pointers for PRG/VROM and load the data! + m_cart->prg_alloc(prg_size, tag()); + memcpy(m_cart->get_prg_base(), get_software_region("prg"), prg_size); + if (vrom_size) + { + m_cart->vrom_alloc(vrom_size, tag()); + memcpy(m_cart->get_vrom_base(), get_software_region("chr"), vrom_size); + } + + // SETUP steps 7: allocate the remaining pointer, when needed + if (vram_size) + m_cart->vram_alloc(vram_size); + if (prgram_size) + m_cart->prgram_alloc(prgram_size); + + // also nes_smb2j_device needs WRAM initialized to 0xff? check! + if (m_pcb_id == UNL_SMB2J) + memset(m_cart->get_prgram_base(), 0xff, prgram_size); + + // Attempt to load a battery file for this ROM + // A few boards have internal RAM with a battery (MMC6, Taito X1-005 & X1-017, etc.) + if (battery_size || mapper_sram_size) + { + UINT32 tot_size = battery_size + mapper_sram_size; + dynamic_buffer temp_nvram(tot_size); + + // some games relies on specific battery patterns to work + // (e.g. Silva Saga does not work with SRAM fully initialized to 0x00) + // and we use the info from xml here to prepare a default NVRAM + dynamic_buffer default_nvram(tot_size); + if (battery_size) + memcpy(&default_nvram[0], get_software_region("bwram"), battery_size); + if (mapper_sram_size) + memset(&default_nvram[battery_size], 0, mapper_sram_size); + + // load battery (using default if no battery exists) + battery_load(&temp_nvram[0], tot_size, &default_nvram[0]); + + // copy battery into PCB arrays + if (battery_size) + { + m_cart->battery_alloc(battery_size); + memcpy(m_cart->get_battery_base(), &temp_nvram[0], battery_size); + } + if (mapper_sram_size) + memcpy(m_cart->get_mapper_sram_base(), &temp_nvram[battery_size], mapper_sram_size); + } +} diff --git a/src/devices/bus/nes/nes_slot.c b/src/devices/bus/nes/nes_slot.c new file mode 100644 index 00000000000..ec1b1f5f4af --- /dev/null +++ b/src/devices/bus/nes/nes_slot.c @@ -0,0 +1,1053 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + Nintendo NES/FC cart emulation + (through slot devices) + + The driver exposes address ranges + 0x4100-0x5fff to read_l/write_l + 0x6000-0x7fff to read_m/write_m (here are *usually* installed NVRAM & WRAM, if any) + 0x8000-0xffff to write_h (reads are directed to 4 x 8K PRG banks) + Default implementations of these handlers are available here, to be rewritten by PCB-specific ones when needed. + + Additional handlers are available, but have to be manually installed at machine_start + * read_ex/write_ex for address range 0x4020-0x40ff + * read_h for address range 0x8000-0xffff when a cart does some protection or address scramble before reading ROM + + PPU exposes address ranges + 0x0000-0x1fff to chr_r/chr_w + 0x2000-0x3eff to nt_r/nt_w + Default implementations of these handlers are available here, to be rewritten by PCB-specific ones when needed. + + Plus a few of latch functions are available: ppu_latch (see MMC2), hblank_irq and scanline_irq (see e.g. MMC3), + but these might be subject to future changes when the PPU is revisited. + + Notes: + - Differently from later systems (like SNES or MD), it is uncommon to find PRG ROMs of NES games which are not a + power of 2K, so we do not perform any mirroring by default. + A bunch of pcb types, though, come with 1.5MB of PRG (some Waixing translations) or with multiple PRG chips + having peculiar size (32K + 16K, 32K + 8K, 32K + 2K). Hence, if such a configuration is detected, we provide + a m_prg_bank_map array to handle internally PRG mirroring up to the next power of 2K, as long as the size is + a multiple of 8K (i.e. the unit chunk for standard PRG). + For the case of PRG chips which are not-multiple of 8K (e.g. UNL-MARIO2-MALEE pcb), the handling has to be + handled in the pcb-specific code! + - Our open bus emulation is very sketchy, by simply returning the higher 8bits of the accessed address. This seems + enough for most games (only two sets have issues with this). A slightly better implementation is almost ready + to fix these two remaining cases, but I plan to revisit the whole implementation in an accurate way at a later + stage + - Bus conflict is implemented based on latest tests by Blargg. There is some uncertainty about AxROM behavior + (some AOROM pcbs suffers from bus conflict, some do not... since no AOROM game is known to glitch due to lack + of bus conflict it seems safe to emulate the board without bus conflict, but eventually it would be good to + differentiate the real variants) + + + Many information about the mappers/pcbs come from the wonderful doc written by Disch. + Current info (when used) are based on v0.6.1 of his docs. + You can find the latest version of the doc at http://www.romhacking.net/docs/362/ + + A lot of details have been based on the researches carried on at NesDev forums (by Blargg, Quietust and many more) + and collected on the NesDev Wiki http://wiki.nesdev.com/ + + Particular thanks go to + - Martin Freij for his work on NEStopia + - Cah4e3 for his efforts on FCEUMM and the reverse engineering of pirate boards + - BootGod, lidnariq and naruko for the PCB tests which made possible + + + ***********************************************************************************************************/ + +/***************************************************************************************** + + A few Mappers suffer of hardware conflict: original dumpers have used the same mapper number for more than + a kind of boards. In these cases (and only in these cases) we exploit nes.hsi to set up accordingly + emulation. Games which requires this hack are the following: + * 032 - Major League needs hardwired mirroring (missing windows and glitched field in top view, otherwise) + * 034 - Impossible Mission II is not like BxROM games (it writes to 0x7ffd-0x7fff, instead of 0x8000). It is still + unplayable though (see above) + * 071 - Fire Hawk is different from other Camerica games (no hardwired mirroring). Without crc_hack no helicopter graphics + * 078 - Cosmo Carrier needs a different mirroring than Holy Diver + * 113 - HES 6-in-1 requires mirroring (check Bookyman playfield), while other games break with this (check AV Soccer) + * 153 - Famicom Jump II uses a different board (or the same in a very different way) + * 242 - DQ8 has no mirroring (missing graphics is due to other reasons though) + + crc_hacks have been added also to handle a few wiring settings which would require submappers: + * CHR protection pins for mapper 185 + * VRC-2, VRC-4 and VRC-6 line wiring + + Remember that the MMC # does not equal the mapper #. In particular, Mapper 4 is + in fact MMC3, Mapper 9 is MMC2 and Mapper 10 is MMC4. Makes perfect sense, right? + + ****************************************************************************************/ + + +#include "emu.h" +#include "hashfile.h" +#include "nes_slot.h" + +#define NES_BATTERY_SIZE 0x2000 + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type NES_CART_SLOT = &device_creator; + + +//************************************************************************** +// NES cartridges Interface +//************************************************************************** + +//------------------------------------------------- +// device_nes_cart_interface - constructor +//------------------------------------------------- + +device_nes_cart_interface::device_nes_cart_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig, device), + m_prg(NULL), + m_vrom(NULL), + m_ciram(NULL), + m_prg_size(0), + m_vrom_size(0), + m_mapper_sram(NULL), + m_mapper_sram_size(0), + m_ce_mask(0), + m_ce_state(0), + m_vrc_ls_prg_a(0), + m_vrc_ls_prg_b(0), + m_vrc_ls_chr(0), + m_mirroring(PPU_MIRROR_NONE), + m_pcb_ctrl_mirror(FALSE), + m_four_screen_vram(FALSE), + m_has_trainer(FALSE), + m_x1_005_alt_mirroring(FALSE), + m_bus_conflict(TRUE), + m_open_bus(0), + m_prg_chunks(0), + m_prg_mask(0xffff), + m_chr_source(CHRRAM), + m_vrom_chunks(0), + m_vram_chunks(0) +{ +} + + +//------------------------------------------------- +// ~device_nes_cart_interface - destructor +//------------------------------------------------- + +device_nes_cart_interface::~device_nes_cart_interface() +{ +} + +//------------------------------------------------- +// pointer allocators +//------------------------------------------------- + +void device_nes_cart_interface::prg_alloc(size_t size, const char *tag) +{ + if (m_prg == NULL) + { + m_prg = device().machine().memory().region_alloc(std::string(tag).append(NESSLOT_PRGROM_REGION_TAG).c_str(), size, 1, ENDIANNESS_LITTLE)->base(); + m_prg_size = size; + m_prg_chunks = size / 0x4000; + if (size % 0x2000) + { + // A few pirate carts have PRG made of 32K + 2K or some weird similar config + // in this case we treat the banking as if this 'extra' PRG is not present and + // the pcb code has to handle it by accessing directly m_prg! + printf("Warning! The loaded PRG has size not a multiple of 8KB (0x%X)\n", (UINT32)size); + m_prg_chunks--; + } + + m_prg_mask = ((m_prg_chunks << 1) - 1); + +// printf("first mask %x!\n", m_prg_mask); + if ((m_prg_chunks << 1) & m_prg_mask) + { + int mask_bits = 0, temp = (m_prg_chunks << 1), mapsize; + // contrary to what happens with later systems, like e.g. SNES or MD, + // only half a dozen of NES carts have PRG which is not a power of 2 + // so we use this bank_map only as an exception +// printf("uneven rom!\n"); + + // 1. redefine mask as (next power of 2)-1 + for (; temp; ) + { + mask_bits++; + temp >>= 1; + } + m_prg_mask = (1 << mask_bits) - 1; +// printf("new mask %x!\n", m_prg_mask); + mapsize = (1 << mask_bits)/2; + + // 2. create a bank_map for banks in the range mask/2 -> mask + m_prg_bank_map.resize(mapsize); + + // 3. fill the bank_map accounting for mirrors + int j; + for (j = mapsize; j < (m_prg_chunks << 1); j++) + m_prg_bank_map[j - mapsize] = j; + + while (j % mapsize) + { + int k = 0, repeat_banks; + while ((j % (mapsize >> k)) && k < mask_bits) + k++; + repeat_banks = j % (mapsize >> (k - 1)); + for (int l = 0; l < repeat_banks; l++) + m_prg_bank_map[(j - mapsize) + l] = m_prg_bank_map[(j - mapsize) + l - repeat_banks]; + j += repeat_banks; + } + + // check bank map! +// for (int i = 0; i < mapsize; i++) +// { +// printf("bank %3d = %3d\t", i, m_prg_bank_map[i]); +// if ((i%8) == 7) +// printf("\n"); +// } + } + } +} + +void device_nes_cart_interface::vrom_alloc(size_t size, const char *tag) +{ + if (m_vrom == NULL) + { + std::string tempstring(tag); + tempstring.append(NESSLOT_CHRROM_REGION_TAG); + m_vrom = device().machine().memory().region_alloc(tempstring.c_str(), size, 1, ENDIANNESS_LITTLE)->base(); + m_vrom_size = size; + m_vrom_chunks = size / 0x2000; + } +} + +void device_nes_cart_interface::prgram_alloc(size_t size) +{ + m_prgram.resize(size); +} + +void device_nes_cart_interface::vram_alloc(size_t size) +{ + m_vram.resize(size); + m_vram_chunks = size / 0x2000; +} + +void device_nes_cart_interface::battery_alloc(size_t size) +{ + m_battery.resize(size); +} + + +//------------------------------------------------- +// PRG helpers +//------------------------------------------------- + +inline int device_nes_cart_interface::prg_8k_bank_num(int bank_8k) +{ + if (m_prg_mask == ((m_prg_chunks << 1) - 1)) + return bank_8k & m_prg_mask; + + // only a few pirate games (less than a dozen) have PRG which is not power of 2 + // so we treat it here separately, rather than forcing all games to use m_prg_bank_map + + // case 1: if we are accessing a bank before the end of the image, just return that bank + if (bank_8k < ((m_prg_chunks << 1) - 1)) + return bank_8k; + + // case 2: otherwise return a mirror using the bank_map! +// UINT8 temp = bank_8k; + bank_8k &= m_prg_mask; + bank_8k -= (m_prg_mask/2 + 1); +// printf("bank: accessed %x (top: %x), returned %x\n", temp, (m_prg_chunks << 1) - 1, m_prg_bank_map[bank_8k]); + return m_prg_bank_map[bank_8k]; +} + +inline void device_nes_cart_interface::update_prg_banks(int prg_bank_start, int prg_bank_end) +{ + for (int prg_bank = prg_bank_start; prg_bank <= prg_bank_end; prg_bank++) + { + assert(prg_bank >= 0); + assert(prg_bank < ARRAY_LENGTH(m_prg_bank)); + assert(prg_bank < ARRAY_LENGTH(m_prg_bank_mem)); + + m_prg_bank_mem[prg_bank]->set_entry(m_prg_bank[prg_bank]); + } +} + +void device_nes_cart_interface::prg32(int bank) +{ + /* if there is only 16k PRG, return */ + if (!(m_prg_chunks >> 1)) + return; + + /* assumes that bank references a 32k chunk */ + bank = prg_8k_bank_num(bank * 4); + + m_prg_bank[0] = bank + 0; + m_prg_bank[1] = bank + 1; + m_prg_bank[2] = bank + 2; + m_prg_bank[3] = bank + 3; + update_prg_banks(0, 3); +} + +void device_nes_cart_interface::prg16_89ab(int bank) +{ + /* assumes that bank references a 16k chunk */ + bank = prg_8k_bank_num(bank * 2); + + m_prg_bank[0] = bank + 0; + m_prg_bank[1] = bank + 1; + update_prg_banks(0, 1); +} + +void device_nes_cart_interface::prg16_cdef(int bank) +{ + /* assumes that bank references a 16k chunk */ + bank = prg_8k_bank_num(bank * 2); + + m_prg_bank[2] = bank + 0; + m_prg_bank[3] = bank + 1; + update_prg_banks(2, 3); +} + +void device_nes_cart_interface::prg8_89(int bank) +{ + /* assumes that bank references an 8k chunk */ + bank = prg_8k_bank_num(bank); + + m_prg_bank[0] = bank; + update_prg_banks(0, 0); +} + +void device_nes_cart_interface::prg8_ab(int bank) +{ + /* assumes that bank references an 8k chunk */ + bank = prg_8k_bank_num(bank); + + m_prg_bank[1] = bank; + update_prg_banks(1, 1); +} + +void device_nes_cart_interface::prg8_cd(int bank) +{ + /* assumes that bank references an 8k chunk */ + bank = prg_8k_bank_num(bank); + + m_prg_bank[2] = bank; + update_prg_banks(2, 2); +} + +void device_nes_cart_interface::prg8_ef(int bank) +{ + /* assumes that bank references an 8k chunk */ + bank = prg_8k_bank_num(bank); + + m_prg_bank[3] = bank; + update_prg_banks(3, 3); +} + +/* We also define an additional helper to map 8k PRG-ROM to one of the banks (passed as parameter) */ +void device_nes_cart_interface::prg8_x(int start, int bank) +{ + assert(start < 4); + + /* assumes that bank references an 8k chunk */ + bank = prg_8k_bank_num(bank); + + m_prg_bank[start] = bank; + update_prg_banks(start, start); +} + +//------------------------------------------------- +// CHR helpers +//------------------------------------------------- + +inline void device_nes_cart_interface::chr_sanity_check( int source ) +{ + if (source == CHRRAM && m_vram.empty()) + fatalerror("CHRRAM bankswitch with no VRAM\n"); + + if (source == CHRROM && m_vrom == NULL) + fatalerror("CHRROM bankswitch with no VROM\n"); +} + +void device_nes_cart_interface::chr8(int bank, int source) +{ + chr_sanity_check(source); + + if (source == CHRRAM) + { + bank &= (m_vram_chunks - 1); + for (int i = 0; i < 8; i++) + { + m_chr_src[i] = source; + m_chr_orig[i] = (bank * 0x2000) + (i * 0x400); // for save state uses! + m_chr_access[i] = &m_vram[m_chr_orig[i]]; + } + } + else + { + bank &= (m_vrom_chunks - 1); + for (int i = 0; i < 8; i++) + { + m_chr_src[i] = source; + m_chr_orig[i] = (bank * 0x2000) + (i * 0x400); // for save state uses! + m_chr_access[i] = &m_vrom[m_chr_orig[i]]; + } + } +} + +void device_nes_cart_interface::chr4_x(int start, int bank, int source) +{ + chr_sanity_check(source); + + if (source == CHRRAM) + { + bank &= ((m_vram_chunks << 1) - 1); + for (int i = 0; i < 4; i++) + { + m_chr_src[i + start] = source; + m_chr_orig[i + start] = (bank * 0x1000) + (i * 0x400); // for save state uses! + m_chr_access[i + start] = &m_vram[m_chr_orig[i + start]]; + } + } + else + { + bank &= ((m_vrom_chunks << 1) - 1); + for (int i = 0; i < 4; i++) + { + m_chr_src[i + start] = source; + m_chr_orig[i + start] = (bank * 0x1000) + (i * 0x400); // for save state uses! + m_chr_access[i + start] = &m_vrom[m_chr_orig[i + start]]; + } + } +} + +void device_nes_cart_interface::chr2_x(int start, int bank, int source) +{ + chr_sanity_check(source); + + if (source == CHRRAM) + { + bank &= ((m_vram_chunks << 2) - 1); + for (int i = 0; i < 2; i++) + { + m_chr_src[i + start] = source; + m_chr_orig[i + start] = (bank * 0x800) + (i * 0x400); // for save state uses! + m_chr_access[i + start] = &m_vram[m_chr_orig[i + start]]; + } + } + else + { + bank &= ((m_vrom_chunks << 2) - 1); + for (int i = 0; i < 2; i++) + { + m_chr_src[i + start] = source; + m_chr_orig[i + start] = (bank * 0x800) + (i * 0x400); // for save state uses! + m_chr_access[i + start] = &m_vrom[m_chr_orig[i + start]]; + } + } +} + +void device_nes_cart_interface::chr1_x(int start, int bank, int source) +{ + chr_sanity_check(source); + + if (source == CHRRAM) + { + bank &= ((m_vram_chunks << 3) - 1); + m_chr_src[start] = source; + m_chr_orig[start] = (bank * 0x400); // for save state uses! + m_chr_access[start] = &m_vram[m_chr_orig[start]]; + } + else + { + bank &= ((m_vrom_chunks << 3) - 1); + m_chr_src[start] = source; + m_chr_orig[start] = (bank * 0x400); // for save state uses! + m_chr_access[start] = &m_vrom[m_chr_orig[start]]; + } +} + +//------------------------------------------------- +// NT & Mirroring helpers +//------------------------------------------------- + +void device_nes_cart_interface::set_nt_page(int page, int source, int bank, int writable) +{ + UINT8* base_ptr; + + switch (source) + { + case CART_NTRAM: + base_ptr = &m_ext_ntram[0]; + break; + case VROM: + bank &= ((m_vrom_chunks << 3) - 1); + base_ptr = m_vrom; + break; + case EXRAM: + case MMC5FILL: + base_ptr = NULL; + break; + case CIRAM: + default: + base_ptr = m_ciram; + break; + } + + page &= 3; /* mask down to the 4 logical pages */ + m_nt_src[page] = source; + + if (base_ptr) + { + m_nt_orig[page] = bank * 0x400; + m_nt_access[page] = base_ptr + m_nt_orig[page]; + } + + m_nt_writable[page] = writable; +} + +void device_nes_cart_interface::set_nt_mirroring(int mirroring) +{ + /* setup our videomem handlers based on mirroring */ + switch (mirroring) + { + case PPU_MIRROR_VERT: + set_nt_page(0, CIRAM, 0, 1); + set_nt_page(1, CIRAM, 1, 1); + set_nt_page(2, CIRAM, 0, 1); + set_nt_page(3, CIRAM, 1, 1); + break; + + case PPU_MIRROR_HORZ: + set_nt_page(0, CIRAM, 0, 1); + set_nt_page(1, CIRAM, 0, 1); + set_nt_page(2, CIRAM, 1, 1); + set_nt_page(3, CIRAM, 1, 1); + break; + + case PPU_MIRROR_HIGH: + set_nt_page(0, CIRAM, 1, 1); + set_nt_page(1, CIRAM, 1, 1); + set_nt_page(2, CIRAM, 1, 1); + set_nt_page(3, CIRAM, 1, 1); + break; + + case PPU_MIRROR_LOW: + set_nt_page(0, CIRAM, 0, 1); + set_nt_page(1, CIRAM, 0, 1); + set_nt_page(2, CIRAM, 0, 1); + set_nt_page(3, CIRAM, 0, 1); + break; + + case PPU_MIRROR_4SCREEN: + if (m_ext_ntram.empty()) fatalerror("4-screen mirroring without on-cart NTRAM!\n"); + set_nt_page(0, CART_NTRAM, 0, 1); + set_nt_page(1, CART_NTRAM, 1, 1); + set_nt_page(2, CART_NTRAM, 2, 1); + set_nt_page(3, CART_NTRAM, 3, 1); + break; + + case PPU_MIRROR_NONE: + default: + set_nt_page(0, CIRAM, 0, 1); + set_nt_page(1, CIRAM, 0, 1); + set_nt_page(2, CIRAM, 1, 1); + set_nt_page(3, CIRAM, 1, 1); + break; + } +} + +//------------------------------------------------- +// Other helpers +//------------------------------------------------- + +// Helper function for the few mappers reading from 0x8000-0xffff for protection +// so that they can access the ROM after the protection handling (which overwrites +// the memory banks) +UINT8 device_nes_cart_interface::hi_access_rom(UINT32 offset) +{ + int bank = (offset & 0x6000) >> 13; + return m_prg[m_prg_bank[bank] * 0x2000 + (offset & 0x1fff)]; +} + +// Helper function for the few mappers subject to bus conflict at write. +// Tests by blargg showed that in many of the boards suffering of CPU/ROM +// conflicts the behaviour can be accurately emulated by writing not the +// original data, but data & rom[offset] +UINT8 device_nes_cart_interface::account_bus_conflict(UINT32 offset, UINT8 data) +{ + // pirate variants of boards subject to bus conflict are often not subject to it + // so we allow to set m_bus_conflict to FALSE at loading time when necessary + if (m_bus_conflict) + return data & hi_access_rom(offset); + else + return data; +} + + +//------------------------------------------------- +// PPU accessors +//------------------------------------------------- + +WRITE8_MEMBER(device_nes_cart_interface::chr_w) +{ + int bank = offset >> 10; + + if (m_chr_src[bank] == CHRRAM) + m_chr_access[bank][offset & 0x3ff] = data; +} + +READ8_MEMBER(device_nes_cart_interface::chr_r) +{ + int bank = offset >> 10; + return m_chr_access[bank][offset & 0x3ff]; +} + + +WRITE8_MEMBER(device_nes_cart_interface::nt_w) +{ + int page = ((offset & 0xc00) >> 10); + + if (!m_nt_writable[page]) + return; + + m_nt_access[page][offset & 0x3ff] = data; +} + +READ8_MEMBER(device_nes_cart_interface::nt_r) +{ + int page = ((offset & 0xc00) >> 10); + return m_nt_access[page][offset & 0x3ff]; +} + + +//------------------------------------------------- +// Base memory accessors (emulating open bus +// behaviour and/or WRAM accesses) +// Open bus emulation is defective, but it should +// be enough for the few cases known to rely on +// this (more in the comments at the top of the +// source) +//------------------------------------------------- + +READ8_MEMBER(device_nes_cart_interface::read_l) +{ + return m_open_bus; +} + +READ8_MEMBER(device_nes_cart_interface::read_m) +{ + if (!m_battery.empty()) + return m_battery[offset & (m_battery.size() - 1)]; + if (!m_prgram.empty()) + return m_prgram[offset & (m_prgram.size() - 1)]; + + return m_open_bus; +} + +WRITE8_MEMBER(device_nes_cart_interface::write_l) +{ +} + +WRITE8_MEMBER(device_nes_cart_interface::write_m) +{ + if (!m_battery.empty()) + m_battery[offset & (m_battery.size() - 1)] = data; + if (!m_prgram.empty()) + m_prgram[offset & (m_prgram.size() - 1)] = data; +} + +WRITE8_MEMBER(device_nes_cart_interface::write_h) +{ +} + + +void device_nes_cart_interface::pcb_start(running_machine &machine, UINT8 *ciram_ptr, bool cart_mounted) +{ + // HACK: to reduce tagmap lookups for PPU-related IRQs, we add a hook to the + // main NES CPU here, even if it does not belong to this device. + m_maincpu = machine.device("maincpu"); + + if (cart_mounted) // disksys expansion can arrive here without the memory banks! + { + // Setup PRG + m_prg_bank_mem[0] = machine.root_device().membank("prg0"); + m_prg_bank_mem[1] = machine.root_device().membank("prg1"); + m_prg_bank_mem[2] = machine.root_device().membank("prg2"); + m_prg_bank_mem[3] = machine.root_device().membank("prg3"); + for (int i = 0; i < 4; i++) + { + if (m_prg_bank_mem[i]) + { + m_prg_bank_mem[i]->configure_entries(0, m_prg_size / 0x2000, m_prg, 0x2000); + m_prg_bank_mem[i]->set_entry(i); + m_prg_bank[i] = i; + } + } + } + + // Setup CHR (VRAM can be present also without PRG rom) + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + chr8(0, m_chr_source); + + // Setup NT + m_ciram = ciram_ptr; + + if (m_four_screen_vram) + { + m_ext_ntram.resize(0x2000); + device().save_item(NAME(m_ext_ntram)); + } + + // at loading time we have configured m_mirroring, now setup NT pages + set_nt_mirroring(m_mirroring); + + // save the on-cart RAM pointers + if (!m_prgram.empty()) + device().save_item(NAME(m_prgram)); + if (!m_vram.empty()) + device().save_item(NAME(m_vram)); + if (!m_battery.empty()) + device().save_item(NAME(m_battery)); +} + +void device_nes_cart_interface::pcb_reg_postload(running_machine &machine) +{ + machine.save().register_postload(save_prepost_delegate(FUNC(device_nes_cart_interface::nes_banks_restore), this)); +} + +void device_nes_cart_interface::nes_banks_restore() +{ + for (int i = 0; i < 4; i++) + m_prg_bank_mem[i]->set_entry(m_prg_bank[i]); + + for (int i = 0; i < 8; i++) + chr1_x(i, m_chr_orig[i] / 0x400, m_chr_src[i]); + + for (int i = 0; i < 4; i++) + set_nt_page(i, m_nt_src[i], m_nt_orig[i] / 0x400, m_nt_writable[i]); +} + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// nes_cart_slot_device - constructor +//------------------------------------------------- +nes_cart_slot_device::nes_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, NES_CART_SLOT, "NES Cartridge Slot", tag, owner, clock, "nes_cart_slot", __FILE__), + device_image_interface(mconfig, *this), + device_slot_interface(mconfig, *this), + m_crc_hack(0), + m_pcb_id(NO_BOARD), + m_must_be_loaded(1) +{ +} + +//------------------------------------------------- +// nes_cart_slot_device - destructor +//------------------------------------------------- + +nes_cart_slot_device::~nes_cart_slot_device() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void nes_cart_slot_device::device_start() +{ + m_cart = dynamic_cast(get_card_device()); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void nes_cart_slot_device::device_config_complete() +{ + // set brief and instance name + update_names(); +} + + +void nes_cart_slot_device::pcb_start(UINT8 *ciram_ptr) +{ + if (m_cart) + m_cart->pcb_start(machine(), ciram_ptr, exists()); +} + +void nes_cart_slot_device::pcb_reset() +{ + if (m_cart) + m_cart->pcb_reset(); +} + +/*------------------------------------------------- + call load + -------------------------------------------------*/ + + +/*------------------------------------------------- + + Load from xml list and identify the required slot device + + -------------------------------------------------*/ + +/* Include emulation of NES PCBs for softlist */ +#include "nes_pcb.inc" + + +/*------------------------------------------------- + + Load .unf files (UNIF boards) and identify the required slot device + + -------------------------------------------------*/ + +/* Include emulation of UNIF Boards for .unf files */ +#include "nes_unif.inc" + + +/*------------------------------------------------- + + Load .nes files (iNES mappers) and identify the required slot devices + + -------------------------------------------------*/ + +/* Include emulation of iNES Mappers for .nes files */ +#include "nes_ines.inc" + + +bool nes_cart_slot_device::call_load() +{ + if (m_cart) + { + if (software_entry() == NULL) + { + char magic[4]; + + /* Check first 4 bytes of the image to decide if it is UNIF or iNES */ + /* Unfortunately, many .unf files have been released as .nes, so we cannot rely on extensions only */ + fread(magic, 4); + + if ((magic[0] == 'N') && (magic[1] == 'E') && (magic[2] == 'S')) /* If header starts with 'NES' it is iNES */ + { + if (length() <= 0x10) + { + logerror("%s only contains the iNES header and no data.\n", filename()); + return IMAGE_INIT_FAIL; + } + + call_load_ines(); + } + else if ((magic[0] == 'U') && (magic[1] == 'N') && (magic[2] == 'I') && (magic[3] == 'F')) /* If header starts with 'UNIF' it is UNIF */ + { + if (length() <= 0x20) + { + logerror("%s only contains the UNIF header and no data.\n", filename()); + return IMAGE_INIT_FAIL; + } + + call_load_unif(); + } + else + { + logerror("%s is NOT a file in either iNES or UNIF format.\n", filename()); + return IMAGE_INIT_FAIL; + } + } + else + call_load_pcb(); + } + + return IMAGE_INIT_PASS; +} + + +/*------------------------------------------------- + call_unload + -------------------------------------------------*/ + +void nes_cart_slot_device::call_unload() +{ + if (m_cart) + { + if (m_cart->get_battery_size() || m_cart->get_mapper_sram_size()) + { + UINT32 tot_size = m_cart->get_battery_size() + m_cart->get_mapper_sram_size(); + dynamic_buffer temp_nvram(tot_size); + if (m_cart->get_battery_size()) + memcpy(&temp_nvram[0], m_cart->get_battery_base(), m_cart->get_battery_size()); + if (m_cart->get_mapper_sram_size()) + memcpy(&temp_nvram[m_cart->get_battery_size()], m_cart->get_mapper_sram_base(), m_cart->get_mapper_sram_size()); + + battery_save(&temp_nvram[0], tot_size); + } + } +} + + +/*------------------------------------------------- + call softlist load + -------------------------------------------------*/ + +bool nes_cart_slot_device::call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry) +{ + load_software_part_region(*this, swlist, swname, start_entry); + return TRUE; +} + +/*------------------------------------------------- + get default card software + -------------------------------------------------*/ + +void nes_cart_slot_device::get_default_card_software(std::string &result) +{ + if (open_image_file(mconfig().options())) + { + const char *slot_string = "nrom"; + UINT32 len = core_fsize(m_file); + dynamic_buffer rom(len); + + core_fread(m_file, &rom[0], len); + + if ((rom[0] == 'N') && (rom[1] == 'E') && (rom[2] == 'S')) + slot_string = get_default_card_ines(&rom[0], len); + + if ((rom[0] == 'U') && (rom[1] == 'N') && (rom[2] == 'I') && (rom[3] == 'F')) + slot_string = get_default_card_unif(&rom[0], len); + + clear(); + + result.assign(slot_string); + } + else + software_get_default_slot(result, "nrom"); +} + + +/*------------------------------------------------- + read + -------------------------------------------------*/ + +READ8_MEMBER(nes_cart_slot_device::read_l) +{ + if (m_cart) + { + UINT8 val = m_cart->read_l(space, offset); + // update open bus + m_cart->set_open_bus(((offset + 0x4100) & 0xff00) >> 8); + return val; + } + else + return 0xff; +} + +READ8_MEMBER(nes_cart_slot_device::read_m) +{ + if (m_cart) + { + UINT8 val = m_cart->read_m(space, offset); + // update open bus + m_cart->set_open_bus(((offset + 0x6000) & 0xff00) >> 8); + return val; + } + else + return 0xff; +} + +READ8_MEMBER(nes_cart_slot_device::read_h) +{ + if (m_cart) + { + UINT8 val = m_cart->read_h(space, offset); + // update open bus + m_cart->set_open_bus(((offset + 0x8000) & 0xff00) >> 8); + return val; + } + else + return 0xff; +} + +READ8_MEMBER(nes_cart_slot_device::read_ex) +{ + if (m_cart) + { + UINT8 val = m_cart->read_ex(space, offset); + // update open bus + m_cart->set_open_bus(((offset + 0x4020) & 0xff00) >> 8); + return val; + } + else + return 0xff; +} + + +/*------------------------------------------------- + write + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_cart_slot_device::write_l) +{ + if (m_cart) + { + m_cart->write_l(space, offset, data); + // update open bus + m_cart->set_open_bus(((offset + 0x4100) & 0xff00) >> 8); + } +} + +WRITE8_MEMBER(nes_cart_slot_device::write_m) +{ + if (m_cart) + { + m_cart->write_m(space, offset, data); + // update open bus + m_cart->set_open_bus(((offset + 0x6000) & 0xff00) >> 8); + } +} + +WRITE8_MEMBER(nes_cart_slot_device::write_h) +{ + if (m_cart) + { + m_cart->write_h(space, offset, data); + // update open bus + m_cart->set_open_bus(((offset + 0x8000) & 0xff00) >> 8); + } +} + +WRITE8_MEMBER(nes_cart_slot_device::write_ex) +{ + if (m_cart) + { + m_cart->write_ex(space, offset, data); + // update open bus + m_cart->set_open_bus(((offset + 0x4020) & 0xff00) >> 8); + } +} + + +//------------------------------------------------- +// partial hash function to be used by +// device_image_partialhash_func +//------------------------------------------------- + +void nes_partialhash(hash_collection &dest, const unsigned char *data, + unsigned long length, const char *functions) +{ + if (length <= 16) + return; + dest.compute(&data[16], length - 16, functions); +} diff --git a/src/devices/bus/nes/nes_slot.h b/src/devices/bus/nes/nes_slot.h new file mode 100644 index 00000000000..f959465af33 --- /dev/null +++ b/src/devices/bus/nes/nes_slot.h @@ -0,0 +1,430 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_SLOT_H__ +#define __NES_SLOT_H__ + +/*************************************************************************** + TYPE DEFINITIONS + ***************************************************************************/ + +// uncomment this for *very* verbose logging of most cart accesses +//#define NES_PCB_DEBUG + +/* Boards */ +enum +{ + STD_NROM = 0, + STD_AXROM, STD_AMROM, STD_BXROM, + STD_CNROM, STD_CPROM, + STD_EXROM, STD_FXROM, STD_GXROM, + STD_HKROM, STD_PXROM, + STD_SXROM, STD_TXROM, STD_TXSROM, + STD_TKROM, STD_TQROM, + STD_UXROM, STD_UN1ROM, UXROM_CC, + HVC_FAMBASIC, NES_QJ, PAL_ZZ, STD_EVENT, + STD_SXROM_A, STD_SOROM, STD_SOROM_A, + STD_DISKSYS, + STD_NROM368,//homebrew extension of NROM! + /* Discrete components boards (by various manufacturer) */ + DIS_74X161X138, DIS_74X139X74, + DIS_74X377, DIS_74X161X161X32, + /* Active Enterprises */ + ACTENT_ACT52, + /* AGCI */ + AGCI_50282, + /* AVE */ + AVE_NINA01, AVE_NINA06, AVE_MAXI15, + /* Bandai */ + BANDAI_FJUMP2, BANDAI_PT554, + BANDAI_DATACH, BANDAI_KARAOKE, BANDAI_OEKAKIDS, + BANDAI_FCG, BANDAI_LZ93, BANDAI_LZ93EX1, BANDAI_LZ93EX2, + /* Caltron */ + CALTRON_6IN1, + /* Camerica */ + CAMERICA_BF9093, CAMERICA_BF9096, CAMERICA_ALADDIN, + CAMERICA_GOLDENFIVE, GG_NROM, + /* Dreamtech */ + DREAMTECH_BOARD, + /* Irem */ + IREM_G101, IREM_H3001, IREM_LROG017, + IREM_TAM_S1, IREM_HOLYDIVR, + /* Jaleco */ + JALECO_SS88006, JALECO_JF11, JALECO_JF13, + JALECO_JF16, JALECO_JF17, JALECO_JF17_ADPCM, + JALECO_JF19, JALECO_JF19_ADPCM, JALECO_JF23, + JALECO_JF24, JALECO_JF29, JALECO_JF33, + /* Konami */ + KONAMI_VRC1, KONAMI_VRC2, KONAMI_VRC3, + KONAMI_VRC4, KONAMI_VRC6, KONAMI_VRC7, + /* Namcot */ + NAMCOT_163, NAMCOT_175, NAMCOT_340, + NAMCOT_3425, NAMCOT_34X3, NAMCOT_3446, + /* NTDEC */ + NTDEC_ASDER, NTDEC_FIGHTINGHERO, + /* Rex Soft */ + REXSOFT_SL1632, REXSOFT_DBZ5, + /* Sachen */ + SACHEN_8259A, SACHEN_8259B, SACHEN_8259C, SACHEN_8259D, + SACHEN_SA009, SACHEN_SA0036, SACHEN_SA0037, + SACHEN_SA72007, SACHEN_SA72008, SACHEN_TCA01, + SACHEN_TCU01, SACHEN_TCU02, SACHEN_SA9602B, + SACHEN_74LS374, SACHEN_74LS374_ALT, SACHEN_SHERO, + /* Sunsoft */ + SUNSOFT_1, SUNSOFT_2, SUNSOFT_3, SUNSOFT_4, + SUNSOFT_DCS, SUNSOFT_5, SUNSOFT_FME7, + /* Taito */ + TAITO_TC0190FMC, TAITO_TC0190FMCP, + TAITO_X1_005, TAITO_X1_017, + /* Tengen */ + TENGEN_800008, TENGEN_800032, TENGEN_800037, + /* TXC */ + TXC_22211, TXC_DUMARACING, TXC_MJBLOCK, + TXC_COMMANDOS, TXC_TW, TXC_STRIKEW, + /* Multigame Carts */ + BMC_64IN1NR, BMC_190IN1, BMC_A65AS, + BMC_HIK8IN1, BMC_NOVEL1, BMC_NOVEL2, BMC_S24IN1SC03, BMC_T262, + BMC_WS, BMC_SUPERBIG_7IN1, BMC_SUPERHIK_4IN1, BMC_BALLGAMES_11IN1, + BMC_MARIOPARTY_7IN1, BMC_GOLD_7IN1, BMC_SUPER_700IN1, BMC_FAMILY_4646, + BMC_36IN1, BMC_21IN1, BMC_150IN1, BMC_35IN1, BMC_64IN1, + BMC_15IN1, BMC_SUPERHIK_300IN1, BMC_SUPERGUN_20IN1, + BMC_GOLDENCARD_6IN1, BMC_72IN1, BMC_SUPER_42IN1, BMC_76IN1, + BMC_31IN1, BMC_22GAMES, BMC_20IN1, BMC_110IN1, + BMC_70IN1, BMC_800IN1, BMC_1200IN1, + BMC_GKA, BMC_GKB, BMC_VT5201, BMC_BENSHIENG, BMC_810544, + BMC_NTD_03, BMC_G63IN1, BMC_FK23C, BMC_FK23CA, BMC_PJOY84, + BMC_POWERFUL_255, BMC_11160, BMC_G146, BMC_8157, BMC_830118C, + BMC_411120C, BMC_GOLD150, BMC_GOLD260, BMC_CH001, BMC_SUPER22, + BMC_12IN1, BMC_4IN1RESET, BMC_42IN1RESET, + /* Unlicensed */ + UNL_8237, UNL_CC21, UNL_AX5705, UNL_KOF97, UNL_KS7057, + UNL_N625092, UNL_SC127, UNL_SMB2J, UNL_T230, UNL_MMALEE, + UNL_UXROM, UNL_MK2, UNL_XIAOZY, UNL_KOF96, + UNL_SF3, UNL_RACERMATE, UNL_EDU2K, UNL_LH53, UNL_LH32, UNL_LH10, + UNL_STUDYNGAME, UNL_603_5052, UNL_H2288, UNL_2708, + UNL_MALISB, UNL_BB, UNL_AC08, UNL_A9746, UNL_WORLDHERO, + UNL_43272, UNL_TF1201, UNL_CITYFIGHT, + /* Bootleg boards */ + BTL_SMB2JA, BTL_MARIOBABY, BTL_AISENSHINICOL, BTL_TOBIDASE, + BTL_SMB2JB, BTL_09034A, BTL_SMB3, BTL_SBROS11, BTL_DRAGONNINJA, + BTL_PIKACHUY2K, BTL_SHUIGUAN, + /* Misc: these are needed to convert mappers to boards, I will sort them later */ + OPENCORP_DAOU306, HES_BOARD, SVISION16_BOARD, RUMBLESTATION_BOARD, JYCOMPANY_A, JYCOMPANY_B, JYCOMPANY_C, + MAGICSERIES_MD, KASING_BOARD, FUTUREMEDIA_BOARD, FUKUTAKE_BOARD, SOMARI_SL12, + HENGG_SRICH, HENGG_XHZS, HENGG_SHJY3, SUBOR_TYPE0, SUBOR_TYPE1, + KAISER_KS7058, KAISER_KS7032, KAISER_KS7022, KAISER_KS7017, + KAISER_KS7012, KAISER_KS7013B, KAISER_KS202, KAISER_KS7031, + CNE_DECATHLON, CNE_FSB, CNE_SHLZ, CONY_BOARD, YOKO_BOARD, + RCM_GS2015, RCM_GS2004, RCM_GS2013, RCM_TF9IN1, RCM_3DBLOCK, + WAIXING_TYPE_A, WAIXING_TYPE_A1, WAIXING_TYPE_B, WAIXING_TYPE_C, WAIXING_TYPE_D, + WAIXING_TYPE_E, WAIXING_TYPE_F, WAIXING_TYPE_G, WAIXING_TYPE_H, WAIXING_TYPE_H1, + WAIXING_TYPE_I, WAIXING_TYPE_J, WAIXING_FS304, + WAIXING_SGZLZ, WAIXING_SGZ, WAIXING_WXZS, WAIXING_SECURITY, WAIXING_SH2, + WAIXING_DQ8, WAIXING_FFV, WAIXING_WXZS2, SUPERGAME_LIONKING, SUPERGAME_BOOGERMAN, + KAY_BOARD, HOSENKAN_BOARD, NITRA_TDA, GOUDER_37017, NANJING_BOARD, + WHIRLWIND_2706, + NOCASH_NOCHR, // homebrew PCB design which uses NTRAM for CHRRAM + BTL_ACTION53, // homebrew PCB for homebrew multicarts + BTL_2A03_PURITANS, // homebrew PCB + /* FFE boards, for mappers 6, 8, 17 */ + FFE3_BOARD, FFE4_BOARD, FFE8_BOARD, TEST_BOARD, + /* Unsupported (for place-holder boards, with no working emulation) & no-board (at init) */ + UNSUPPORTED_BOARD, UNKNOWN_BOARD, NO_BOARD +}; + + +#define CHRROM 0 +#define CHRRAM 1 + + +#define CIRAM 0 +#define VROM 1 +#define EXRAM 2 +#define MMC5FILL 3 +#define CART_NTRAM 4 + + +#define PPU_MIRROR_NONE 0 +#define PPU_MIRROR_VERT 1 +#define PPU_MIRROR_HORZ 2 +#define PPU_MIRROR_HIGH 3 +#define PPU_MIRROR_LOW 4 +#define PPU_MIRROR_4SCREEN 5 + + +// ======================> device_nes_cart_interface + +class device_nes_cart_interface : public device_slot_card_interface +{ +public: + // construction/destruction + device_nes_cart_interface(const machine_config &mconfig, device_t &device); + virtual ~device_nes_cart_interface(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h) { return 0xff; } + virtual DECLARE_READ8_MEMBER(read_ex) { return 0xff; } + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual DECLARE_WRITE8_MEMBER(write_ex) { } + + virtual DECLARE_READ8_MEMBER(chr_r); + virtual DECLARE_WRITE8_MEMBER(chr_w); + virtual DECLARE_READ8_MEMBER(nt_r); + virtual DECLARE_WRITE8_MEMBER(nt_w); + + // hack until disk system is made modern! + virtual void disk_flip_side() { } + + void prg_alloc(size_t size, const char *tag); + void vrom_alloc(size_t size, const char *tag); + void prgram_alloc(size_t size); + void vram_alloc(size_t size); + void battery_alloc(size_t size); + + int get_mirroring() { return m_mirroring; } + void set_mirroring(int val) { m_mirroring = val; } + bool get_pcb_ctrl_mirror() { return m_pcb_ctrl_mirror; } + void set_pcb_ctrl_mirror(bool val) { m_pcb_ctrl_mirror = val; } + bool get_four_screen_vram() { return m_four_screen_vram; } + void set_four_screen_vram(bool val) { m_four_screen_vram = val; } + bool get_trainer() { return m_has_trainer; } + void set_trainer(bool val) { m_has_trainer = val; } + + void set_ce(int mask, int state) { m_ce_mask = mask; m_ce_state = state; } + void set_vrc_lines(int PRG_A, int PRG_B, int CHR) { m_vrc_ls_prg_a = PRG_A; m_vrc_ls_prg_b = PRG_B; m_vrc_ls_chr = CHR; } + void set_x1_005_alt(bool val) { m_x1_005_alt_mirroring = val; } + void set_bus_conflict(bool val) { m_bus_conflict = val; } + void set_open_bus(UINT8 val) { m_open_bus = val; } + + UINT8* get_prg_base() { return m_prg; } + UINT8* get_prgram_base() { return &m_prgram[0]; } + UINT8* get_vrom_base() { return m_vrom; } + UINT8* get_vram_base() { return &m_vram[0]; } + UINT8* get_battery_base() { return &m_battery[0]; } + UINT8* get_mapper_sram_base() { return m_mapper_sram; } + + UINT32 get_prg_size() { return m_prg_size; } + UINT32 get_prgram_size() { return m_prgram.size(); } + UINT32 get_vrom_size() { return m_vrom_size; } + UINT32 get_vram_size() { return m_vram.size(); } + UINT32 get_battery_size() { return m_battery.size(); } + UINT32 get_mapper_sram_size() { return m_mapper_sram_size; } + + virtual void ppu_latch(offs_t offset) {} + virtual void hblank_irq(int scanline, int vblank, int blanked) {} + virtual void scanline_irq(int scanline, int vblank, int blanked) {} + + virtual void pcb_reset() {} // many pcb expect specific PRG/CHR banking at start + virtual void pcb_start(running_machine &machine, UINT8 *ciram_ptr, bool cart_mounted); + void pcb_reg_postload(running_machine &machine); + void nes_banks_restore(); + + UINT8 hi_access_rom(UINT32 offset); // helper ROM access for a bunch of PCB reading 0x8000-0xffff for protection too + UINT8 account_bus_conflict(UINT32 offset, UINT8 data); + +protected: + + // internal state + UINT8 *m_prg; + UINT8 *m_vrom; + UINT8 *m_ciram; + dynamic_buffer m_prgram; + dynamic_buffer m_vram; + dynamic_buffer m_battery; + UINT32 m_prg_size; + UINT32 m_vrom_size; + + // HACK: to reduce tagmap lookups for PPU-related IRQs, we add a hook to the + // main NES CPU here, even if it does not belong to this device. + cpu_device *m_maincpu; + + // these are specific of some boards but must be accessible from the driver + // E.g. additional save ram for HKROM, X1-005 & X1-017 boards, or ExRAM for MMC5 + UINT8 *m_mapper_sram; + dynamic_buffer m_ext_ntram; + UINT32 m_mapper_sram_size; + + int m_ce_mask; + int m_ce_state; + int m_vrc_ls_prg_a; + int m_vrc_ls_prg_b; + int m_vrc_ls_chr; + + int m_mirroring; + bool m_pcb_ctrl_mirror, m_four_screen_vram, m_has_trainer; + bool m_x1_005_alt_mirroring; // temp hack for two kind of mirroring in Taito X1-005 boards (to be replaced with pin checking) + bool m_bus_conflict; + UINT8 m_open_bus; + + // PRG + inline int prg_8k_bank_num(int bank); + inline void update_prg_banks(int prg_bank_start, int prg_bank_end); + memory_bank *m_prg_bank_mem[4]; + int m_prg_bank[4]; + UINT32 m_prg_chunks; + UINT32 m_prg_mask; + + // PRG helpers + void prg32(int bank); + void prg16_89ab(int bank); + void prg16_cdef(int bank); + void prg8_89(int bank); + void prg8_ab(int bank); + void prg8_cd(int bank); + void prg8_ef(int bank); + void prg8_x(int start, int bank); + + + // CHR + int m_chr_source; // global source for the 8 VROM banks + inline void chr_sanity_check(int source); + + //these were previously called chr_map. they are a quick banking structure, + //because some of these change multiple times per scanline! + int m_chr_src[8]; //defines source of base pointer + int m_chr_orig[8]; //defines offset of 0x400 byte segment at base pointer + UINT8 *m_chr_access[8]; //source translated + origin -> valid pointer! + + UINT32 m_vrom_chunks; + UINT32 m_vram_chunks; + + // CHR helpers + void chr8(int bank, int source); + void chr4_x(int start, int bank, int source); + void chr4_0(int bank, int source){ chr4_x(0, bank, source); }; + void chr4_4(int bank, int source){ chr4_x(4, bank, source); }; + void chr2_x(int start, int bank, int source); + void chr2_0(int bank, int source) { chr2_x(0, bank, source); }; + void chr2_2(int bank, int source) { chr2_x(2, bank, source); }; + void chr2_4(int bank, int source) { chr2_x(4, bank, source); }; + void chr2_6(int bank, int source) { chr2_x(6, bank, source); }; + void chr1_x(int start, int bank, int source); + void chr1_0(int bank, int source) { chr1_x(0, bank, source); }; + void chr1_1(int bank, int source) { chr1_x(1, bank, source); }; + void chr1_2(int bank, int source) { chr1_x(2, bank, source); }; + void chr1_3(int bank, int source) { chr1_x(3, bank, source); }; + void chr1_4(int bank, int source) { chr1_x(4, bank, source); }; + void chr1_5(int bank, int source) { chr1_x(5, bank, source); }; + void chr1_6(int bank, int source) { chr1_x(6, bank, source); }; + void chr1_7(int bank, int source) { chr1_x(7, bank, source); }; + + + // NameTable & Mirroring + //these were previously called nt_page. they are a quick banking structure for a maximum of 4K of RAM/ROM/ExRAM + int m_nt_src[4]; + int m_nt_orig[4]; + int m_nt_writable[4]; + UINT8 *m_nt_access[4]; //quick banking structure for a maximum of 4K of RAM/ROM/ExRAM + + void set_nt_page(int page, int source, int bank, int writable); + void set_nt_mirroring(int mirroring); + + std::vector m_prg_bank_map; +}; + +void nes_partialhash(hash_collection &dest, const unsigned char *data, unsigned long length, const char *functions); + +// ======================> nes_cart_slot_device + +class nes_cart_slot_device : public device_t, + public device_image_interface, + public device_slot_interface +{ +public: + // construction/destruction + nes_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual ~nes_cart_slot_device(); + + // device-level overrides + virtual void device_start(); + virtual void device_config_complete(); + + // image-level overrides + virtual bool call_load(); + virtual void call_unload(); + virtual bool call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry); + + void call_load_ines(); + void call_load_unif(); + void call_load_pcb(); + + virtual iodevice_t image_type() const { return IO_CARTSLOT; } + virtual bool is_readable() const { return 1; } + virtual bool is_writeable() const { return 0; } + virtual bool is_creatable() const { return 0; } + virtual bool must_be_loaded() const { return m_must_be_loaded; } + virtual bool is_reset_on_load() const { return 1; } + virtual const char *image_interface() const { return "nes_cart"; } + virtual const char *file_extensions() const { return "nes,unf,unif"; } + virtual const option_guide *create_option_guide() const { return NULL; } + virtual device_image_partialhash_func get_partial_hash() const { return &nes_partialhash; } + + // slot interface overrides + virtual void get_default_card_software(std::string &result); + const char * get_default_card_ines(UINT8 *ROM, UINT32 len); + const char * get_default_card_unif(UINT8 *ROM, UINT32 len); + const char * nes_get_slot(int pcb_id); + int nes_get_pcb_id(const char *slot); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_READ8_MEMBER(read_ex); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual DECLARE_WRITE8_MEMBER(write_ex); + + // hack until disk system is made modern! + virtual void disk_flip_side() { if (m_cart) m_cart->disk_flip_side(); } + + int get_pcb_id() { return m_pcb_id; }; + + void pcb_start(UINT8 *ciram_ptr); + void pcb_reset(); + + // temporarily here + int m_crc_hack; + + int get_crc_hack() { return m_crc_hack; }; + + void set_must_be_loaded(bool _must_be_loaded) { m_must_be_loaded = _must_be_loaded; } + + //private: + + device_nes_cart_interface* m_cart; + int m_pcb_id; + bool m_must_be_loaded; +}; + +// device type definition +extern const device_type NES_CART_SLOT; + + +/*************************************************************************** + DEVICE CONFIGURATION MACROS + ***************************************************************************/ + +#define NESSLOT_PRGROM_REGION_TAG ":cart:prg_rom" +#define NESSLOT_CHRROM_REGION_TAG ":cart:chr_rom" + + +#define MCFG_NES_CARTRIDGE_ADD(_tag, _slot_intf, _def_slot) \ + MCFG_DEVICE_ADD(_tag, NES_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) + +#define MCFG_NES_CARTRIDGE_NOT_MANDATORY \ + static_cast(device)->set_must_be_loaded(FALSE); + + +// Hacky configuration to add a slot with fixed disksys interface +#define MCFG_DISKSYS_ADD(_tag, _slot_intf, _def_slot) \ + MCFG_DEVICE_ADD(_tag, NES_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, true) \ + MCFG_NES_CARTRIDGE_NOT_MANDATORY + +#endif diff --git a/src/devices/bus/nes/nes_unif.inc b/src/devices/bus/nes/nes_unif.inc new file mode 100644 index 00000000000..b501c590159 --- /dev/null +++ b/src/devices/bus/nes/nes_unif.inc @@ -0,0 +1,575 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/***************************************************************************************** + + NES MMC Emulation - UNIF boards + + Very preliminary support for UNIF boards + + TODO: + - properly support WRAM, etc. + +****************************************************************************************/ + + +/* Set to generate prg & chr files when the cart is loaded */ +#define SPLIT_PRG 0 +#define SPLIT_CHR 0 + + +/************************************************************* + + unif_list + + Supported UNIF boards and corresponding handlers + + *************************************************************/ + + +struct unif +{ + const char *board; /* UNIF board */ + + int nvwram; + int wram; + int chrram; + int board_idx; +}; + + +/* CHRRAM sizes */ +enum +{ + CHRRAM_0 = 0, + CHRRAM_1, + CHRRAM_2, + CHRRAM_4, + CHRRAM_6, + CHRRAM_8, + CHRRAM_16, + CHRRAM_32 +}; + +static const unif unif_list[] = +{ +/* UNIF NVW WRAM CRAM IDX*/ + { "DREAMTECH01", 0, 0, CHRRAM_8, DREAMTECH_BOARD}, //UNIF only! + { "NES-ANROM", 0, 0, CHRRAM_8, STD_AXROM}, + { "NES-AOROM", 0, 0, CHRRAM_8, STD_AXROM}, + { "NES-CNROM", 0, 0, CHRRAM_0, STD_CNROM}, + { "NES-NROM", 0, 0, CHRRAM_0, STD_NROM}, + { "NES-NROM-128", 0, 0, CHRRAM_0, STD_NROM}, + { "NES-NROM-256", 0, 0, CHRRAM_0, STD_NROM}, + { "NES-NTBROM", 8, 0, CHRRAM_0, SUNSOFT_DCS}, + { "NES-SLROM", 0, 0, CHRRAM_0, STD_SXROM}, + { "NES-TBROM", 0, 0, CHRRAM_0, STD_TXROM}, + { "NES-TFROM", 0, 0, CHRRAM_0, STD_TXROM}, + { "NES-TKROM", 8, 0, CHRRAM_0, STD_TXROM}, + { "NES-TLROM", 0, 0, CHRRAM_0, STD_TXROM}, + { "NES-UOROM", 0, 0, CHRRAM_8, STD_UXROM}, + { "UNL-22211", 0, 0, CHRRAM_0, TXC_22211}, + // mapper 172 & 173 are variant of this one... no UNIF? + { "UNL-KOF97", 0, 0, CHRRAM_0, UNL_KOF97}, + { "UNL-SA-NROM", 0, 0, CHRRAM_0, SACHEN_TCA01}, + { "UNL-VRC7", 0, 0, CHRRAM_0, KONAMI_VRC7}, + { "UNL-T-230", 0, 0, CHRRAM_8, UNL_T230}, + { "UNL-CC-21", 0, 0, CHRRAM_0, UNL_CC21}, + { "UNL-AX5705", 0, 0, CHRRAM_0, UNL_AX5705}, + { "UNL-SMB2J", 8, 8, CHRRAM_0, UNL_SMB2J}, + { "UNL-8237", 0, 0, CHRRAM_0, UNL_8237}, + { "UNL-SL1632", 0, 0, CHRRAM_0, REXSOFT_SL1632}, + { "UNL-SACHEN-74LS374N", 0, 0, CHRRAM_0, SACHEN_74LS374}, + // mapper 243 variant exists! how to distinguish?!? mapper243_l_w, NULL, NULL, NULL, NULL, NULL, NULL (also uses NT_VERT!) + { "UNL-TC-U01-1.5M", 0, 0, CHRRAM_0, SACHEN_TCU01}, + { "UNL-SACHEN-8259C", 0, 0, CHRRAM_0, SACHEN_8259C}, + { "UNL-SA-016-1M", 0, 0, CHRRAM_0, AVE_NINA06}, // actually this is Mapper 146, but works like 79! + { "UNL-SACHEN-8259D", 0, 0, CHRRAM_0, SACHEN_8259D}, + { "UNL-SA-72007", 0, 0, CHRRAM_0, SACHEN_SA72007}, + { "UNL-SA-72008", 0, 0, CHRRAM_0, SACHEN_SA72008}, + { "UNL-SA-0037", 0, 0, CHRRAM_0, SACHEN_SA0037}, + { "UNL-SA-0036", 0, 0, CHRRAM_0, SACHEN_SA0036}, + { "UNL-SA-9602B", 0, 0, CHRRAM_0, SACHEN_SA9602B}, + { "UNL-SACHEN-8259A", 0, 0, CHRRAM_0, SACHEN_8259A}, + { "UNL-SACHEN-8259B", 0, 0, CHRRAM_0, SACHEN_8259B}, + { "BMC-190IN1", 0, 0, CHRRAM_0, BMC_190IN1}, + { "BMC-64IN1NOREPEAT", 0, 0, CHRRAM_0, BMC_64IN1NR}, //UNIF only! + { "BMC-A65AS", 0, 0, CHRRAM_8, BMC_A65AS}, //UNIF only! + { "BMC-GS-2004", 0, 0, CHRRAM_8, RCM_GS2004}, //UNIF only! + { "BMC-GS-2013", 0, 0, CHRRAM_8, RCM_GS2013}, //UNIF only! + { "BMC-NOVELDIAMOND9999999IN1", 0, 0, CHRRAM_0, BMC_NOVEL1}, + { "BMC-SUPER24IN1SC03", 8, 0, CHRRAM_8, BMC_S24IN1SC03}, + { "BMC-SUPERHIK8IN1", 8, 0, CHRRAM_0, BMC_HIK8IN1}, + { "BMC-T-262", 0, 0, CHRRAM_8, BMC_T262}, //UNIF only! + { "BMC-WS", 0, 0, CHRRAM_0, BMC_WS}, //UNIF only! + { "BMC-N625092", 0, 0, CHRRAM_0, UNL_N625092}, + // below are boards which are not yet supported, but are used by some UNIF files. they are here as a reminder to what is missing to be added + { "UNL-TEK90", 0, 0, CHRRAM_0, JYCOMPANY_A}, // JY Company A (is TEK90 the real PCB name?) + { "UNL-KS7017", 0, 0, CHRRAM_0, KAISER_KS7017}, + { "UNL-KS7032", 0, 0, CHRRAM_0, KAISER_KS7032}, // mapper 142 + { "UNL-603-5052", 0, 0, CHRRAM_0, UNL_603_5052}, // mapper 238? + { "UNL-EDU2000", 32, 0, CHRRAM_8, UNL_EDU2K}, + { "UNL-H2288", 0, 0, CHRRAM_0, UNL_H2288}, // mapper 123 + { "UNL-SHERO", 0, 0, CHRRAM_8, SACHEN_SHERO}, + { "UNL-YOKO", 0, 0, CHRRAM_0, YOKO_BOARD}, // similar to mapper 83, but not the same + { "UNL-FS304", 0, 8, CHRRAM_8, WAIXING_FS304}, // used in Zelda 3 by Waixing + { "UNL-43272", 0, 0, CHRRAM_0, UNL_43272}, // used in Gaau Hok Gwong Cheung + { "BTL-MARIO1-MALEE2", 0, 0, CHRRAM_0, UNL_MMALEE}, // mapper 55? + { "BMC-FK23C", 0, 0, CHRRAM_0, BMC_FK23C}, + { "BMC-FK23CA", 0, 0, CHRRAM_0, BMC_FK23CA}, + { "BMC-GHOSTBUSTERS63IN1", 0, 0, CHRRAM_8, BMC_G63IN1 }, + { "BMC-BS-5", 0, 0, CHRRAM_0, BMC_BENSHIENG}, + { "BMC-810544-C-A1", 0, 0, CHRRAM_0, BMC_810544}, + { "BMC-411120-C", 0, 0, CHRRAM_0, BMC_411120C}, + { "BMC-8157", 0, 0, CHRRAM_8, BMC_8157}, + { "BMC-830118C", 0, 0, CHRRAM_0, BMC_830118C}, + { "BMC-D1038", 0, 0, CHRRAM_0, BMC_VT5201}, // mapper 60? + { "BMC-SUPERVISION16IN1", 0, 0, CHRRAM_0, SVISION16_BOARD}, // mapper 53 + { "BMC-NTD-03", 0, 0, CHRRAM_0, BMC_NTD_03}, + { "UNL-AC08", 0, 0, CHRRAM_0, UNL_AC08}, + { "UNL-BB", 0, 0, CHRRAM_0, UNL_BB}, + { "UNL-LH32", 0, 0, CHRRAM_0, UNL_LH32}, + { "UNL-LH53", 0, 0, CHRRAM_0, UNL_LH53}, + { "BMC-G-146", 0, 0, CHRRAM_0, BMC_G146}, + { "BMC-11160", 0, 0, CHRRAM_0, BMC_11160}, + { "UNL-MALISB", 0, 0, CHRRAM_0, UNL_MALISB}, + { "UNL-TF1201", 0, 0, CHRRAM_0, UNL_TF1201}, + { "BMC-12-IN-1", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, + { "BMC-70IN1", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, // mapper 236? + { "BMC-70IN1B", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, // mapper 236? + { "BMC-42IN1RESETSWITCH", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, // mapper 60? + { "PEC-586", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, + { "UNL-DANCE", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, + { "UNL-DRIPGAME", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, // [by Quietust - we need more info] + { "UNL-CITYFIGHT", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, + { "UNL-OneBus", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, + { "UNL-DANCE2000", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD} +}; + +const unif *nes_unif_lookup( const char *board ) +{ + int i; + for (i = 0; i < ARRAY_LENGTH(unif_list); i++) + { + if (!core_stricmp(unif_list[i].board, board)) + return &unif_list[i]; + } + return NULL; +} + +/************************************************************* + + unif_mapr_setup + + setup the board specific variables (wram, nvwram, pcb_id etc.) + for a given board (after reading the MAPR chunk of the UNIF file) + + *************************************************************/ + +void unif_mapr_setup( const char *board, int *pcb_id, int *battery, int *prgram, int *vram_chunks ) +{ + const unif *unif_board = nes_unif_lookup(board); + if (unif_board == NULL) + fatalerror("Unknown UNIF board %s.\n", board); + + *pcb_id = unif_board->board_idx; + *battery = unif_board->nvwram; // we should implement battery banks based on the size of this... + *prgram = unif_board->wram; // we should implement WRAM banks based on the size of this... + + if (unif_board->chrram <= CHRRAM_8) + *vram_chunks = 1; + else if (unif_board->chrram == CHRRAM_16) + *vram_chunks = 2; + else if (unif_board->chrram == CHRRAM_32) + *vram_chunks = 4; +} + + +/************************************************************* + + call_load_unif + + *************************************************************/ + +void nes_cart_slot_device::call_load_unif() +{ + UINT32 vram_size = 0, prgram_size = 0, battery_size = 0, mapper_sram_size = 0; + // SETUP step 1: running through the file and getting PRG, VROM sizes + UINT32 unif_ver = 0, chunk_length = 0, read_length = 0x20; + UINT32 prg_start = 0, chr_start = 0; + UINT32 size = length(), prg_size = 0, vrom_size = 0; + UINT8 buffer[4], mirror = 0; + char magic2[4]; + char unif_mapr[32]; // here we should store MAPR chunks + bool mapr_chunk_found = FALSE, small_prg = FALSE; + + // allocate space to temporarily store PRG & CHR banks + dynamic_buffer temp_prg(256 * 0x4000); + dynamic_buffer temp_chr(256 * 0x2000); + UINT8 temp_byte = 0; + + fseek(4, SEEK_SET); + fread(&buffer, 4); + unif_ver = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + logerror("Loaded game in UNIF format, version %d\n", unif_ver); + + do + { + fseek(read_length, SEEK_SET); + + memset(magic2, '\0', sizeof(magic2)); + fread(&magic2, 4); + + /* We first run through the whole image to find a [MAPR] chunk. This is needed + because, unfortunately, the MAPR chunk is not always the first chunk (see + Super 24-in-1). When such a chunk is found, we set mapr_chunk_found=1 and + we go back to load other chunks! */ + if (!mapr_chunk_found) + { + if ((magic2[0] == 'M') && (magic2[1] == 'A') && (magic2[2] == 'P') && (magic2[3] == 'R')) + { + mapr_chunk_found = TRUE; + logerror("[MAPR] chunk found: "); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + if (chunk_length <= 0x20) + fread(&unif_mapr, chunk_length); + logerror("%s\n", unif_mapr); + + /* now that we found the MAPR chunk, we can go back to load other chunks */ + fseek(0x20, SEEK_SET); + read_length = 0x20; + } + else + { + logerror("Skip this chunk. We need a [MAPR] chunk before anything else.\n"); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + read_length += (chunk_length + 8); + } + } + else + { + /* What kind of chunk do we have here? */ + if ((magic2[0] == 'M') && (magic2[1] == 'A') && (magic2[2] == 'P') && (magic2[3] == 'R')) + { + /* The [MAPR] chunk has already been read, so we skip it */ + /* TO DO: it would be nice to check if more than one MAPR chunk is present */ + logerror("[MAPR] chunk found (in the 2nd run). Already loaded.\n"); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'R') && (magic2[1] == 'E') && (magic2[2] == 'A') && (magic2[3] == 'D')) + { + logerror("[READ] chunk found. No support yet.\n"); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'N') && (magic2[1] == 'A') && (magic2[2] == 'M') && (magic2[3] == 'E')) + { + logerror("[NAME] chunk found. No support yet.\n"); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'W') && (magic2[1] == 'R') && (magic2[2] == 'T') && (magic2[3] == 'R')) + { + logerror("[WRTR] chunk found. No support yet.\n"); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'T') && (magic2[1] == 'V') && (magic2[2] == 'C') && (magic2[3] == 'I')) + { + logerror("[TVCI] chunk found.\n"); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + fread(&temp_byte, 1); + logerror("Television Standard : %s\n", (temp_byte == 0) ? "NTSC" : (temp_byte == 1) ? "PAL" : "Does not matter"); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'T') && (magic2[1] == 'V') && (magic2[2] == 'S') && (magic2[3] == 'C')) // is this the same as TVCI?? + { + logerror("[TVSC] chunk found. No support yet.\n"); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'D') && (magic2[1] == 'I') && (magic2[2] == 'N') && (magic2[3] == 'F')) + { + logerror("[DINF] chunk found. No support yet.\n"); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'C') && (magic2[1] == 'T') && (magic2[2] == 'R') && (magic2[3] == 'L')) + { + logerror("[CTRL] chunk found. No support yet.\n"); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'B') && (magic2[1] == 'A') && (magic2[2] == 'T') && (magic2[3] == 'R')) + { + logerror("[BATR] chunk found. No support yet.\n"); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'V') && (magic2[1] == 'R') && (magic2[2] == 'O') && (magic2[3] == 'R')) + { + logerror("[VROR] chunk found. No support yet.\n"); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'M') && (magic2[1] == 'I') && (magic2[2] == 'R') && (magic2[3] == 'R')) + { + logerror("[MIRR] chunk found.\n"); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + fread(&mirror, 1); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'P') && (magic2[1] == 'C') && (magic2[2] == 'K')) + { + logerror("[PCK%c] chunk found. No support yet.\n", magic2[3]); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'C') && (magic2[1] == 'C') && (magic2[2] == 'K')) + { + logerror("[CCK%c] chunk found. No support yet.\n", magic2[3]); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'P') && (magic2[1] == 'R') && (magic2[2] == 'G')) + { + logerror("[PRG%c] chunk found. ", magic2[3]); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + prg_size += chunk_length; + + if (chunk_length / 0x4000) + logerror("It consists of %d 16K-blocks.\n", chunk_length / 0x4000); + else + { + small_prg = TRUE; + logerror("This chunk is smaller than 16K: the emulation might have issues. Please report this file to the MESS forums.\n"); + } + + /* Read in the program chunks */ + fread(&temp_prg[prg_start], chunk_length); + + prg_start += chunk_length; + read_length += (chunk_length + 8); + } + else if ((magic2[0] == 'C') && (magic2[1] == 'H') && (magic2[2] == 'R')) + { + logerror("[CHR%c] chunk found. ", magic2[3]); + fread(&buffer, 4); + chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); + vrom_size += chunk_length; + + logerror("It consists of %d 8K-blocks.\n", chunk_length / 0x2000); + + /* Read in the vrom chunks */ + fread(&temp_chr[chr_start], chunk_length); + + chr_start += chunk_length; + read_length += (chunk_length + 8); + } + else + { + logerror("Unsupported UNIF chunk or corrupted header. Please report the problem at MESS Board.\n"); + read_length = size; + } + } + } while (size > read_length); + + if (!mapr_chunk_found) + { + fatalerror("UNIF should have a [MAPR] chunk to work. Check if your image has been corrupted\n"); + } + + if (!prg_start) + { + fatalerror("No PRG found. Please report the problem at MESS Board.\n"); + } + + // SETUP step 2: getting PCB and other settings + int pcb_id = 0, battery = 0, prgram = 0, vram_chunks = 0; + unif_mapr_setup(unif_mapr, &pcb_id, &battery, &prgram, &vram_chunks); + + // SETUP step 3: storing the info needed for emulation + m_pcb_id = pcb_id; + if (battery) + battery_size = NES_BATTERY_SIZE; // we should allow for smaller battery! + prgram_size = prgram * 0x2000; + vram_size = vram_chunks * 0x2000; + + m_cart->set_four_screen_vram(FALSE); + switch (mirror) + { + case 0: // Horizontal Mirroring (Hard Wired) + m_cart->set_mirroring(PPU_MIRROR_HORZ); + break; + case 1: // Vertical Mirroring (Hard Wired) + m_cart->set_mirroring(PPU_MIRROR_VERT); + break; + case 2: // Mirror All Pages From $2000 (Hard Wired) + m_cart->set_mirroring(PPU_MIRROR_LOW); + break; + case 3: // Mirror All Pages From $2400 (Hard Wired) + m_cart->set_mirroring(PPU_MIRROR_HIGH); + break; + case 4: // Four Screens of VRAM (Hard Wired) + m_cart->set_four_screen_vram(TRUE); + m_cart->set_mirroring(PPU_MIRROR_4SCREEN); + break; + case 5: // Mirroring Controlled By Mapper Hardware + logerror("Mirroring handled by the board hardware.\n"); + // default to horizontal at start + m_cart->set_mirroring(PPU_MIRROR_HORZ); + break; + default: + logerror("Undocumented mirroring value.\n"); + // default to horizontal + m_cart->set_mirroring(PPU_MIRROR_HORZ); + break; + } + + // SETUP step 4: logging what we have found + logerror("-- Board %s\n", unif_mapr); + logerror("-- PRG 0x%x (%d x 16k chunks)\n", prg_size, prg_size / 0x4000); + logerror("-- VROM 0x%x (%d x 8k chunks)\n", vrom_size, vrom_size / 0x2000); + logerror("-- VRAM 0x%x (%d x 8k chunks)\n", vram_size, vram_size / 0x2000); + + // SETUP steps 5/6: allocate pointers for PRG/VROM and load the data! + if (prg_size == 0x4000) + { + m_cart->prg_alloc(0x8000, tag()); + memcpy(m_cart->get_prg_base(), &temp_prg[0], 0x4000); + memcpy(m_cart->get_prg_base() + 0x4000, m_cart->get_prg_base(), 0x4000); + } + else + { + m_cart->prg_alloc(prg_size, tag()); + memcpy(m_cart->get_prg_base(), &temp_prg[0], prg_size); + } + + if (small_prg) // This is not supported yet, so warn users about this + osd_printf_error("Loaded UNIF file with non-16k PRG chunk. This is not supported in MESS yet."); + + if (vrom_size) + { + m_cart->vrom_alloc(vrom_size, tag()); + memcpy(m_cart->get_vrom_base(), &temp_chr[0], vrom_size); + } + +#if SPLIT_PRG + { + FILE *prgout; + char outname[255]; + + sprintf(outname, "%s.prg", filename()); + prgout = fopen(outname, "wb"); + if (prgout) + { + fwrite(m_cart->get_prg_base(), 1, 0x4000 * m_cart->get_prg_size(), prgout); + osd_printf_error("Created PRG chunk\n"); + } + + fclose(prgout); + } +#endif + +#if SPLIT_CHR + if (state->m_chr_chunks > 0) + { + FILE *chrout; + char outname[255]; + + sprintf(outname, "%s.chr", filename()); + chrout= fopen(outname, "wb"); + if (chrout) + { + fwrite(m_cart->get_vrom_base(), 1, m_cart->get_vrom_size(), chrout); + osd_printf_error("Created CHR chunk\n"); + } + fclose(chrout); + } +#endif + // SETUP steps 7: allocate the remaining pointer, when needed + if (vram_size) + m_cart->vram_alloc(vram_size); + if (prgram_size) + m_cart->prgram_alloc(prgram_size); + + // Attempt to load a battery file for this ROM + // A few boards have internal RAM with a battery (MMC6, Taito X1-005 & X1-017, etc.) + if (battery_size || mapper_sram_size) + { + UINT32 tot_size = battery_size + mapper_sram_size; + dynamic_buffer temp_nvram(tot_size); + battery_load(&temp_nvram[0], tot_size, 0x00); + if (battery_size) + { + m_cart->battery_alloc(battery_size); + memcpy(m_cart->get_battery_base(), &temp_nvram[0], battery_size); + } + if (mapper_sram_size) + memcpy(m_cart->get_mapper_sram_base(), &temp_nvram[battery_size], mapper_sram_size); + } + + logerror("UNIF support is only very preliminary.\n"); +} + +const char * nes_cart_slot_device::get_default_card_unif(UINT8 *ROM, UINT32 len) +{ + UINT32 chunk_length = 0, read_length = 0x20; + int pcb_id = 0, battery = 0, prgram = 0, vram_chunks = 0; + char unif_mapr[32]; + + do + { + if ((ROM[read_length + 0] == 'M') && (ROM[read_length + 1] == 'A') && (ROM[read_length + 2] == 'P') && (ROM[read_length + 3] == 'R')) + { + chunk_length = ROM[read_length + 4] | (ROM[read_length + 5] << 8) | (ROM[read_length + 6] << 16) | (ROM[read_length + 7] << 24); + + if (chunk_length <= 0x20) + memcpy(unif_mapr, ROM + read_length + 8, chunk_length); + + read_length += (chunk_length + 8); + } + else + { + chunk_length = ROM[read_length + 4] | (ROM[read_length + 5] << 8) | (ROM[read_length + 6] << 16) | (ROM[read_length + 7] << 24); + read_length += (chunk_length + 8); + } + } while (len > read_length); + + unif_mapr_setup(unif_mapr, &pcb_id, &battery, &prgram, &vram_chunks); + + return nes_get_slot(pcb_id); +} diff --git a/src/devices/bus/nes/ntdec.c b/src/devices/bus/nes/ntdec.c new file mode 100644 index 00000000000..4dd7e56a842 --- /dev/null +++ b/src/devices/bus/nes/ntdec.c @@ -0,0 +1,186 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for NTDEC PCBs + + + Here we emulate the following PCBs + + * NTDEC ASDER [mapper 112] + * NTDEC Fighting Hero [mapper 193] + + TODO: + - why is Master Shooter not working? + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "ntdec.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_NTDEC_ASDER = &device_creator; +const device_type NES_NTDEC_FH = &device_creator; + + +nes_ntdec_asder_device::nes_ntdec_asder_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NTDEC_ASDER, "NES Cart NTDEC Asder PCB", tag, owner, clock, "nes_ntdec_asder", __FILE__) +{ +} + +nes_ntdec_fh_device::nes_ntdec_fh_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NTDEC_FH, "NES Cart NTDEC Fighting Hero PCB", tag, owner, clock, "nes_ntdec_fh", __FILE__) +{ +} + + + + +void nes_ntdec_asder_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_ntdec_asder_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; +} + +void nes_ntdec_fh_device::device_start() +{ + common_start(); +} + +void nes_ntdec_fh_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32((m_prg_chunks - 1) >> 1); + chr8(0, m_chr_source); + set_nt_mirroring(PPU_MIRROR_VERT); +} + + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + NTDEC ASDER Bootleg Board + + Games: Cobra Mission, Fighting Hero III, Huang Di, Master + Shooter + + iNES: mapper 112 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_ntdec_asder_device::write_h) +{ + LOG_MMC(("ntdec_asder write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset) + { + case 0x0000: + m_latch = data & 0x07; + break; + case 0x2000: + switch (m_latch) + { + case 0: + prg8_89(data); + break; + case 1: + prg8_ab(data); + break; + case 2: + data &= 0xfe; + chr1_0(data, CHRROM); + chr1_1(data + 1, CHRROM); + break; + case 3: + data &= 0xfe; + chr1_2(data, CHRROM); + chr1_3(data + 1, CHRROM); + break; + case 4: + chr1_4(data, CHRROM); + break; + case 5: + chr1_5(data, CHRROM); + break; + case 6: + chr1_6(data, CHRROM); + break; + case 7: + chr1_7(data, CHRROM); + break; + } + break; + case 0x6000: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + } +} + +/*------------------------------------------------- + + Bootleg Board by NTDEC for Fighting Hero + + Games: Fighting Hero + + Very simple mapper: writes to 0x6000-0x7fff swap PRG and + CHR banks. + + iNES: mapper 193 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_ntdec_fh_device::write_m) +{ + LOG_MMC(("ntdec_fh write_m, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x03) + { + case 0: + chr4_0(data >> 2, CHRROM); + break; + case 1: + chr2_4(data >> 1, CHRROM); + break; + case 2: + chr2_6(data >> 1 , CHRROM); + break; + case 3: + prg8_89(data); + break; + } +} diff --git a/src/devices/bus/nes/ntdec.h b/src/devices/bus/nes/ntdec.h new file mode 100644 index 00000000000..00712eecf1c --- /dev/null +++ b/src/devices/bus/nes/ntdec.h @@ -0,0 +1,51 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_NTDEC_H +#define __NES_NTDEC_H + +#include "nxrom.h" + + +// ======================> nes_ntdec_asder_device + +class nes_ntdec_asder_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ntdec_asder_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_ntdec_fh_device + +class nes_ntdec_fh_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_ntdec_fh_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + + + + + +// device type definition +extern const device_type NES_NTDEC_ASDER; +extern const device_type NES_NTDEC_FH; + +#endif diff --git a/src/devices/bus/nes/nxrom.c b/src/devices/bus/nes/nxrom.c new file mode 100644 index 00000000000..431d384e7f1 --- /dev/null +++ b/src/devices/bus/nes/nxrom.c @@ -0,0 +1,583 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Nintendo xxROM PCBs + + + Here we emulate the following PCBs + + * Nintendo NROM [mapper 0] + * Nintendo AxROM [mapper 7] + * Nintendo BxROM [mapper 34] + * Nintendo CNROM [mapper 3 & 185] + * Nintendo CPROM [mapper 13] + * Nintendo GxROM/MxROM [mapper 66] + * Nintendo UxROM [mapper 2] + Crazy Climbers variant [mapper 180] + * Nintendo UN1ROM [mapper 94] + + Known issues on specific mappers: + + * 000 F1 Race requires more precise PPU timing. It currently has plenty of 1-line glitches. + * 003 Firehouse Rescue has flashing graphics (same PPU issue as Back to the Future 2 & 3?) + * 007 Marble Madness has small graphics corruptions + * 034 Titanic 1912 (pirate BxROM) has missing gfx (same PPU issue of many Waixing titles almost for sure) + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "nxrom.h" + +#include "sound/samples.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_NROM = &device_creator; +const device_type NES_NROM368 = &device_creator; +const device_type NES_FCBASIC = &device_creator; +const device_type NES_AXROM = &device_creator; +const device_type NES_BXROM = &device_creator; +const device_type NES_CNROM = &device_creator; +const device_type NES_CPROM = &device_creator; +const device_type NES_GXROM = &device_creator; +const device_type NES_UXROM = &device_creator; +const device_type NES_UXROM_CC = &device_creator; +const device_type NES_UN1ROM = &device_creator; +const device_type NES_NOCHR = &device_creator; + + +nes_nrom_device::nes_nrom_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) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_nes_cart_interface( mconfig, *this ) +{ +} + +nes_nrom_device::nes_nrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, NES_NROM, "NES Cart NROM PCB", tag, owner, clock, "nes_nrom", __FILE__), + device_nes_cart_interface( mconfig, *this ) +{ +} + +nes_nrom368_device::nes_nrom368_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NROM368, "NES Cart NROM-368 PCB", tag, owner, clock, "nes_nrom368", __FILE__) +{ +} + +nes_fcbasic_device::nes_fcbasic_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_FCBASIC, "NES Cart Famicom BASIC PCB", tag, owner, clock, "nes_fcbasic", __FILE__) +{ +} + +nes_axrom_device::nes_axrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_AXROM, "NES Cart AxROM PCB", tag, owner, clock, "nes_axrom", __FILE__) +{ +} + +nes_bxrom_device::nes_bxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_BXROM, "NES Cart BxROM PCB", tag, owner, clock, "nes_bxrom", __FILE__) +{ +} + +nes_cnrom_device::nes_cnrom_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_cnrom_device::nes_cnrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_CNROM, "NES Cart CNROM PCB", tag, owner, clock, "nes_cnrom", __FILE__) +{ +} + +nes_cprom_device::nes_cprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_CPROM, "NES Cart CPROM PCB", tag, owner, clock, "nes_cprom", __FILE__) +{ +} + +nes_gxrom_device::nes_gxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_GXROM, "NES Cart GxROM PCB", tag, owner, clock, "nes_gxrom", __FILE__) +{ +} + +nes_uxrom_device::nes_uxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_UXROM, "NES Cart UxROM PCB", tag, owner, clock, "nes_uxrom", __FILE__) +{ +} + +nes_uxrom_cc_device::nes_uxrom_cc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_UXROM_CC, "NES Cart UNROM M5 PCB (Crazy Climber)", tag, owner, clock, "nes_uxrom_cc", __FILE__) +{ +} + +nes_un1rom_device::nes_un1rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_UN1ROM, "NES Cart UN1ROM PCB", tag, owner, clock, "nes_un1rom", __FILE__) +{ +} + +nes_nochr_device::nes_nochr_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_NOCHR, "NES Cart NoCash NOCHR PCB", tag, owner, clock, "nes_nochr", __FILE__) +{ +} + + +void nes_nrom_device::common_start() +{ + // PRG + save_item(NAME(m_prg_bank)); + + // CHR + save_item(NAME(m_chr_source)); + save_item(NAME(m_chr_src)); + save_item(NAME(m_chr_orig)); + + // NT + save_item(NAME(m_mirroring)); + save_item(NAME(m_nt_src)); + save_item(NAME(m_nt_orig)); + save_item(NAME(m_nt_writable)); + + // open bus + save_item(NAME(m_open_bus)); +} + +void nes_nrom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_axrom_device::device_start() +{ + common_start(); +} + +void nes_axrom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + set_nt_mirroring(PPU_MIRROR_LOW); +} + +void nes_bxrom_device::device_start() +{ + common_start(); +} + +void nes_bxrom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_cnrom_device::device_start() +{ + common_start(); + save_item(NAME(m_chr_open_bus)); +} + +void nes_cnrom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_chr_open_bus = 0; +} + +void nes_cprom_device::device_start() +{ + common_start(); +} + +void nes_cprom_device::pcb_reset() +{ + m_chr_source = CHRRAM; + prg32(0); + chr4_0(0, m_chr_source); + chr4_4(0, m_chr_source); +} + +void nes_gxrom_device::device_start() +{ + common_start(); +} + +void nes_gxrom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_uxrom_device::device_start() +{ + common_start(); +} + +void nes_uxrom_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); +} + +void nes_uxrom_cc_device::device_start() +{ + common_start(); +} + +void nes_uxrom_cc_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_un1rom_device::device_start() +{ + common_start(); +} + +void nes_un1rom_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); +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + NROM board emulation + + Games: Mario Bros., Super Mario Bros., Tennis and most of + the first generation games + + iNES: mapper 0 + + In MESS: Supported, no need of specific handlers or IRQ + + -------------------------------------------------*/ + +/*------------------------------------------------- + + NROM-368 board emulation + + iNES: mapper 0 with 3xPRG banks + This is an homebrew extension to map linearly 46KB + or PRG in boards with no PRG bankswitch logic + + In MESS: Supported + + -------------------------------------------------*/ + +READ8_MEMBER(nes_nrom368_device::read_l) +{ + LOG_MMC(("nrom368 read_l, offset: %04x\n", offset)); + offset += 0x100; + if (offset >= 0x800) + return m_prg[offset - 0x800]; + else + return m_open_bus; +} + +READ8_MEMBER(nes_nrom368_device::read_m) +{ + LOG_MMC(("nrom368 read_m, offset: %04x\n", offset)); + return m_prg[0x1800 + (offset & 0x1fff)]; +} + +READ8_MEMBER(nes_nrom368_device::read_h) +{ + LOG_MMC(("nrom368 read_h, offset: %04x\n", offset)); + return m_prg[0x3800 + (offset & 0x7fff)]; +} + +/*------------------------------------------------- + + AxROM board emulation + + Games: Arch Rivals, Battletoads, Cabal, Commando, Solstice + + writes to 0x8000-0xffff change PRG banks + sets mirroring + + AMROM has bus conflict + AOROM has generally bus conflict too, but some later pcbs + added some discrete component to disable ROM and avoid + conflicts. No AOROM games is known to glitch due to lack of + bus conflict, so it seems safe to emulate AOROM + without bus conflict. + + iNES: mapper 7 + + In MESS: Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_axrom_device::write_h) +{ + LOG_MMC(("axrom write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW); + prg32(data); +} + +/*------------------------------------------------- + + BxROM board emulation + + writes to 0x8000-0xffff change PRG banks + + iNES: mapper 34 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bxrom_device::write_h) +{ + /* This portion of the mapper is nearly identical to Mapper 7, except no one-screen mirroring */ + /* Deadly Towers is really a BxROM game - the demo screens look wrong using mapper 7. */ + LOG_MMC(("bxrom write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict, but the same is not true for some pirate variants + data = account_bus_conflict(offset, data); + + prg32(data); +} + +/*------------------------------------------------- + + CNROM board emulation + + Games: B-Wings, Mighty Bomb Jack, Seicross, Spy vs. Spy, + Adventure Island, Flipull, Friday 13th, GeGeGe no + Kitarou, Ghostbusters, Gradius, Hokuto no Ken, Milon's + Secret Castle + + writes to 0x8000-0xffff change CHR 8K banks + + missing BC? + + iNES: mappers 3 & 185 (the latter for games using Pins as + protection) + + Notice that BANDAI_PT554 board (Aerobics Studio) uses very + similar hardware but with an additional sound chip which + gets writes to 0x6000 (currently unemulated in MESS) + + In MESS: Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_cnrom_device::write_h) +{ + LOG_MMC(("cxrom write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict, but the same is not true for some pirate variants + data = account_bus_conflict(offset, data); + + if (m_ce_mask) + { + //printf("mask %x state %x\n", m_ce_mask, m_ce_state); + chr8(data & ~m_ce_mask, CHRROM); + + if ((data & m_ce_mask) == m_ce_state) + m_chr_open_bus = 0; + else + m_chr_open_bus = 1; + } + else + chr8(data, CHRROM); +} + +READ8_MEMBER(nes_cnrom_device::chr_r) +{ + int bank = offset >> 10; + + // a few CNROM boards contained copy protection schemes through + // suitably configured diodes, so that subsequent CHR reads can + // give actual VROM content or open bus values. + // For most boards, chr_open_bus remains always zero. + if (m_chr_open_bus) + return m_open_bus; + + return m_chr_access[bank][offset & 0x3ff]; +} + + +/*------------------------------------------------- + + CPROM board emulation + + Games: Videomation + + writes to 0x8000-0xffff change CHR 4K lower banks + + iNES: mapper 13 + + In MESS: Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_cprom_device::write_h) +{ + LOG_MMC(("cprom write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + chr4_4(data, CHRRAM); +} + + +/*------------------------------------------------- + + GxROM/MxROM board emulation + + writes to 0x8000-0xffff change PRG and CHR banks + + iNES: mapper 66 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_gxrom_device::write_h) +{ + LOG_MMC(("gxrom write_h, offset %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + prg32((data & 0xf0) >> 4); + chr8(data & 0x0f, CHRROM); +} + +/*------------------------------------------------- + + UxROM board emulation + + Games: Castlevania, Dragon Quest II, Duck Tales, MegaMan, + Metal Gear + + writes to 0x8000-0xffff change PRG 16K lower banks + + iNES: mapper 2 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_uxrom_device::write_h) +{ + LOG_MMC(("uxrom write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + prg16_89ab(data); +} + +/*------------------------------------------------- + + Nihon Bussan UNROM M5 + + Games: Crazy Climber Jpn + + Very simple mapper: prg16_89ab is always set to bank 0, + while prg16_cdef is set by writes to 0x8000-0xffff. The game + uses a custom controller. + + iNES: mapper 180 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_uxrom_cc_device::write_h) +{ + LOG_MMC(("uxrom_cc write_h, offset: %04x, data: %02x\n", offset, data)); + + prg16_cdef(data); +} + +/*------------------------------------------------- + + UN1ROM board emulation + + Games: Senjou no Okami + + writes to 0x8000-0xffff change PRG 16K lower banks + + iNES: mapper 94 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_un1rom_device::write_h) +{ + LOG_MMC(("un1rom write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + prg16_89ab(data >> 2); +} + +/*------------------------------------------------- + + NoCash NOCHR board emulation + + This is an homebrew PCB design on a single chip + (+possibly CIC) which uses the NTRAM as CHRRAM! + + iNES: mapper 218 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_nochr_device::chr_w) +{ + int mirr = get_mirroring(); + if (mirr == PPU_MIRROR_HIGH) + m_ciram[(offset & 0x3ff) + 0x000] = data; + else if (mirr == PPU_MIRROR_LOW) + m_ciram[(offset & 0x3ff) + 0x400] = data; + else + m_ciram[offset & 0x7ff] = data; // not sure here, since there is no software to test... +} + +READ8_MEMBER(nes_nochr_device::chr_r) +{ + int mirr = get_mirroring(); + if (mirr == PPU_MIRROR_HIGH) + return m_ciram[(offset & 0x3ff) + 0x000]; + else if (mirr == PPU_MIRROR_LOW) + return m_ciram[(offset & 0x3ff) + 0x400]; + else + return m_ciram[offset & 0x7ff]; // not sure here, since there is no software to test... +} diff --git a/src/devices/bus/nes/nxrom.h b/src/devices/bus/nes/nxrom.h new file mode 100644 index 00000000000..fe105be0189 --- /dev/null +++ b/src/devices/bus/nes/nxrom.h @@ -0,0 +1,216 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_MMCX_H +#define __NES_MMCX_H + +#include "nes_slot.h" +#include "sound/samples.h" + + +// ======================> nes_nrom_device + +class nes_nrom_device : public device_t, + public device_nes_cart_interface +{ +public: + // construction/destruction + nes_nrom_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(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual void device_start() { common_start(); } + + virtual void pcb_reset(); + + void common_start(); +}; + + +// ======================> nes_nrom368_device + +class nes_nrom368_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_nrom368_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h); +}; + + +// ======================> nes_fcbasic_device + +class nes_fcbasic_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_fcbasic_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // emulate the additional WRAM +}; + + +// ======================> nes_axrom_device + +class nes_axrom_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_axrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_bxrom_device + +class nes_bxrom_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_bxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_cnrom_device + +class nes_cnrom_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_cnrom_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_cnrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(chr_r); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_chr_open_bus; +}; + + +// ======================> nes_cprom_device + +class nes_cprom_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_cprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_gxrom_device + +class nes_gxrom_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_gxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_uxrom_device + +class nes_uxrom_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_uxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_uxrom_cc_device + +class nes_uxrom_cc_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_uxrom_cc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_un1rom_device + +class nes_un1rom_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_un1rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_nochr_device + +class nes_nochr_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_nochr_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_READ8_MEMBER(chr_r); + virtual DECLARE_WRITE8_MEMBER(chr_w); +}; + + + +// device type definition +extern const device_type NES_NROM; +extern const device_type NES_NROM368; +extern const device_type NES_FCBASIC; +extern const device_type NES_AXROM; +extern const device_type NES_BXROM; +extern const device_type NES_CNROM; +extern const device_type NES_CPROM; +extern const device_type NES_GXROM; +extern const device_type NES_UXROM; +extern const device_type NES_UXROM_CC; +extern const device_type NES_UN1ROM; +extern const device_type NES_NOCHR; + +#endif diff --git a/src/devices/bus/nes/pirate.c b/src/devices/bus/nes/pirate.c new file mode 100644 index 00000000000..557831ed144 --- /dev/null +++ b/src/devices/bus/nes/pirate.c @@ -0,0 +1,1387 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for pirate cart PCBs + + + Here we emulate the various PCBs used by Asian & Korean pirate games + + TODO: + - Are the scrolling glitches (check status bar) in Magic Dragon correct? FWIW, NEStopia behaves similarly + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "pirate.h" + +#include "cpu/m6502/m6502.h" +#include "video/ppu2c0x.h" // this has to be included so that IRQ functions can access PPU_BOTTOM_VISIBLE_SCANLINE + + +#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_AGCI_50282 = &device_creator; +const device_type NES_DREAMTECH01 = &device_creator; +const device_type NES_FUKUTAKE = &device_creator; +const device_type NES_FUTUREMEDIA = &device_creator; +const device_type NES_MAGSERIES = &device_creator; +const device_type NES_DAOU306 = &device_creator; +const device_type NES_SUBOR0 = &device_creator; +const device_type NES_SUBOR1 = &device_creator; +const device_type NES_CC21 = &device_creator; +const device_type NES_XIAOZY = &device_creator; +const device_type NES_EDU2K = &device_creator; +const device_type NES_T230 = &device_creator; +const device_type NES_MK2 = &device_creator; +const device_type NES_WHERO = &device_creator; +const device_type NES_43272 = &device_creator; +const device_type NES_TF1201 = &device_creator; +const device_type NES_CITYFIGHT = &device_creator; + + +nes_agci_device::nes_agci_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_AGCI_50282, "NES Cart AGCI 50282 PCB", tag, owner, clock, "nes_agci50282", __FILE__) +{ +} + +nes_dreamtech_device::nes_dreamtech_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_DREAMTECH01, "NES Cart Dreamtech01 PCB", tag, owner, clock, "nes_dreamtech", __FILE__) +{ +} + +nes_fukutake_device::nes_fukutake_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_FUKUTAKE, "NES Cart Fukutake Study Box PCB", tag, owner, clock, "nes_fukutake", __FILE__) +{ +} + +nes_futuremedia_device::nes_futuremedia_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_FUTUREMEDIA, "NES Cart FutureMedia PCB", tag, owner, clock, "nes_futuremedia", __FILE__) +{ +} + +nes_magseries_device::nes_magseries_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_MAGSERIES, "NES Cart Magical Series PCB", tag, owner, clock, "nes_magseries", __FILE__) +{ +} + +nes_daou306_device::nes_daou306_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_DAOU306, "NES Cart Daou 306 PCB", tag, owner, clock, "nes_daou306", __FILE__) +{ +} + +nes_subor0_device::nes_subor0_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SUBOR0, "NES Cart Subor Type 0 PCB", tag, owner, clock, "nes_subor0", __FILE__) +{ +} + +nes_subor1_device::nes_subor1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SUBOR1, "NES Cart Subor Type 1 PCB", tag, owner, clock, "nes_subor1", __FILE__) +{ +} + +nes_cc21_device::nes_cc21_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_CC21, "NES Cart CC-21 PCB", tag, owner, clock, "nes_cc21", __FILE__) +{ +} + +nes_xiaozy_device::nes_xiaozy_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_XIAOZY, "NES Cart Xiao Zhuan Yuan PCB", tag, owner, clock, "nes_xiaozy", __FILE__) +{ +} + +nes_edu2k_device::nes_edu2k_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_EDU2K, "NES Cart Educational Computer 2000 PCB", tag, owner, clock, "nes_edu2k", __FILE__) +{ +} + +nes_t230_device::nes_t230_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_T230, "NES Cart T-230 PCB", tag, owner, clock, "nes_t230", __FILE__) +{ +} + +nes_mk2_device::nes_mk2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_MK2, "NES Cart Mortal Kombat 2 PCB", tag, owner, clock, "nes_mk2", __FILE__) +{ +} + +nes_whero_device::nes_whero_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_WHERO, "NES Cart World Heroes PCB", tag, owner, clock, "nes_whero", __FILE__) +{ +} + +nes_43272_device::nes_43272_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_43272, "NES Cart UNL-43272 PCB", tag, owner, clock, "nes_43272", __FILE__) +{ +} + +nes_tf1201_device::nes_tf1201_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_TF1201, "NES Cart UNL-TF1201 PCB", tag, owner, clock, "nes_tf1201", __FILE__) +{ +} + +nes_cityfight_device::nes_cityfight_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_TF1201, "NES Cart City Fighter PCB", tag, owner, clock, "nes_cityfight", __FILE__) +{ +} + + + + +void nes_agci_device::device_start() +{ + common_start(); +} + +void nes_agci_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_dreamtech_device::device_start() +{ + common_start(); +} + +void nes_dreamtech_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(8); + chr8(0, m_chr_source); +} + +void nes_fukutake_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + + // 2816 (?) bytes of RAM + save_item(NAME(m_ram)); +} + +void nes_fukutake_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0); + chr8(0, m_chr_source); + + m_latch = 0; +} + +void nes_futuremedia_device::device_start() +{ + common_start(); + save_item(NAME(m_irq_clear)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); +} + +void nes_futuremedia_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_clear = 0; + m_irq_enable = 0; + m_irq_count = m_irq_count_latch = 0; +} + +void nes_magseries_device::device_start() +{ + common_start(); +} + +void nes_magseries_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_daou306_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_daou306_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(m_prg_chunks - 2); + prg16_cdef(m_prg_chunks - 1); + chr8(0, m_chr_source); + set_nt_mirroring(PPU_MIRROR_LOW); + + memset(m_reg, 0, sizeof(m_reg)); +} + +void nes_subor0_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_subor0_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0x20); + chr8(0, m_chr_source); + + memset(m_reg, 0, sizeof(m_reg)); +} + +void nes_subor1_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_subor1_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0x07); + chr8(0, m_chr_source); + + memset(m_reg, 0, sizeof(m_reg)); +} + +void nes_cc21_device::device_start() +{ + common_start(); +} + +void nes_cc21_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_xiaozy_device::device_start() +{ + common_start(); +} + +void nes_xiaozy_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32((m_prg_chunks - 1) >> 1); + chr8(0, m_chr_source); +} + +void nes_edu2k_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_edu2k_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_latch = 0; +} + +void nes_t230_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_mmc_vrom_bank)); +} + +void nes_t230_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 = m_irq_enable_latch = 0; + m_irq_count = m_irq_count_latch = 0; + + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); +} + +void nes_mk2_device::device_start() +{ + common_start(); + save_item(NAME(m_irq_clear)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); +} + +void nes_mk2_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(m_prg_chunks - 1); + prg16_cdef(m_prg_chunks - 1); + chr8(0, m_chr_source); + set_nt_mirroring(PPU_MIRROR_VERT); + + m_irq_clear = 0; + m_irq_enable = 0; + m_irq_count = m_irq_count_latch = 0; +} + + +void nes_whero_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)); + save_item(NAME(m_reg)); + save_item(NAME(m_mmc_vrom_bank)); +} + +void nes_whero_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(m_prg_chunks - 1); + chr8(0x100, m_chr_source); + + m_irq_enable = 0; + m_irq_enable_latch = 0; + m_irq_count = 0; + m_irq_count_latch = 0; + + m_reg = 0; + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); +} + +void nes_43272_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_43272_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32((m_prg_chunks - 1) >> 1); + chr8(0, m_chr_source); + + m_latch = 0x81; +} + +void nes_tf1201_device::device_start() +{ + common_start(); + save_item(NAME(m_prg)); + save_item(NAME(m_swap)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_enable_latch)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_mmc_vrom_bank)); +} + +void nes_tf1201_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)); + m_prg = 0; + m_swap = 0; + m_irq_enable = 0; + m_irq_enable_latch = 0; + m_irq_count = 0; +} + +void nes_cityfight_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_prg_reg)); + save_item(NAME(m_prg_mode)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_mmc_vrom_bank)); +} + +void nes_cityfight_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)); + m_prg_reg = 0; + m_prg_mode = 0; + m_irq_enable = 0; + m_irq_count = 0; +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + AGCI 50282 bootleg board emulation + + Games: Death Race + + iNES: mapper 144 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_agci_device::write_h) +{ + LOG_MMC(("agci write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + UINT8 temp = account_bus_conflict(offset, 0xff); + data = (data & temp) | (temp & 1); + + chr8(data >> 4, CHRROM); + prg32(data); +} + +/*------------------------------------------------- + + Board DREAMTECH01 + + Games: Korean Igo + + In MESS: Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_dreamtech_device::write_l) +{ + LOG_MMC(("dreamtech write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset == 0x1020) /* 0x5020 */ + prg16_89ab(data); +} + +/*------------------------------------------------- + + Bootleg Board by Fukutake + + Games: Study Box + + iNES: mapper 186 + + In MESS: Unsupported. + + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_fukutake_device::write_l) +{ + LOG_MMC(("fukutake write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset >= 0x200 && offset < 0x400) + { + if (offset & 1) + prg16_89ab(data); + else + m_latch = data >> 6; + } + else if (offset >= 0x400 && offset < 0xf00) + m_ram[offset - 0x400] = data; +} + +READ8_MEMBER(nes_fukutake_device::read_l) +{ + LOG_MMC(("fukutake read_l, offset: %04x\n", offset)); + offset += 0x100; + + if (offset >= 0x200 && offset < 0x400) + { + if (offset == 0x200 || offset == 0x201 || offset == 0x203) + return 0x00; + else if (offset == 0x202) + return 0x40; + else + return 0xff; + } + else if (offset >= 0x400 && offset < 0xf00) + return m_ram[offset - 0x400]; + + return 0; +} + +WRITE8_MEMBER(nes_fukutake_device::write_m) +{ + LOG_MMC(("fukutake write_m, offset: %04x, data: %02x\n", offset, data)); + m_prgram[((m_latch * 0x2000) + offset) & (m_prgram.size() - 1)] = data; +} + +READ8_MEMBER(nes_fukutake_device::read_m) +{ + LOG_MMC(("fukutake read_m, offset: %04x\n", offset)); + return m_prgram[((m_latch * 0x2000) + offset) & (m_prgram.size() - 1)]; +} + +/*------------------------------------------------- + + Bootleg Board by Future Media + + Games: Crayon Shin-chan (C), San Guo Zhi 4 - Chi Bi Feng Yun + + iNES: mapper 117 + + In MESS: Unsupported. + + -------------------------------------------------*/ + +void nes_futuremedia_device::hblank_irq(int scanline, int vblank, int blanked) +{ + // if (scanline < PPU_BOTTOM_VISIBLE_SCANLINE) + { + if (m_irq_enable && m_irq_count) + { + m_irq_count--; + if (!m_irq_count) + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + } + } +} + +WRITE8_MEMBER(nes_futuremedia_device::write_h) +{ + LOG_MMC(("futuremedia write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset) + { + case 0x0000: + prg8_89(data); + break; + case 0x0001: + prg8_ab(data); + break; + case 0x0002: + prg8_cd(data); + break; + case 0x0003: + prg8_ef(data); + break; + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + case 0x2004: + case 0x2005: + case 0x2006: + case 0x2007: + chr1_x(offset & 0x07, data, CHRROM); + break; + + case 0x5000: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + + case 0x4001: + m_irq_count_latch = data; + break; + case 0x4002: + // IRQ cleared + break; + case 0x4003: + m_irq_count = m_irq_count_latch; + break; + case 0x6000: + m_irq_enable = data & 0x01; + break; + } +} + +/*------------------------------------------------- + + Bootleg Board by Magic Series + + Games: Magic Dragon + + Very simple mapper: writes to 0x8000-0xffff set prg32 and chr8 + banks + + iNES: mapper 107 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_magseries_device::write_h) +{ + LOG_MMC(("magseries write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(data >> 1); + chr8(data, CHRROM); +} + +/*------------------------------------------------- + + Open Corp DAOU306 board + + Games: Metal Force (K) + + iNES: mapper 156 + + In MESS: Supported. + + Notes: Metal Force and Buzz & Waldog only use the first 4 + regs and no mirroring. Janggun ui Adeul uses all features + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_daou306_device::write_h) +{ + LOG_MMC(("daou306 write_h, offset: %04x, data: %02x\n", offset, data)); + int reg = BIT(offset, 2) ? 8 : 0; + + switch (offset) + { + case 0x4000: + case 0x4004: + m_reg[reg + 0] = data; + chr1_0(m_reg[0] | (m_reg[8] << 8), CHRROM); + break; + case 0x4001: + case 0x4005: + m_reg[reg + 1] = data; + chr1_1(m_reg[1] | (m_reg[9] << 8), CHRROM); + break; + case 0x4002: + case 0x4006: + m_reg[reg + 2] = data; + chr1_2(m_reg[2] | (m_reg[10] << 8), CHRROM); + break; + case 0x4003: + case 0x4007: + m_reg[reg + 3] = data; + chr1_3(m_reg[3] | (m_reg[11] << 8), CHRROM); + break; + case 0x4008: + case 0x400c: + m_reg[reg + 4] = data; + chr1_4(m_reg[4] | (m_reg[12] << 8), CHRROM); + break; + case 0x4009: + case 0x400d: + m_reg[reg + 5] = data; + chr1_5(m_reg[5] | (m_reg[13] << 8), CHRROM); + break; + case 0x400a: + case 0x400e: + m_reg[reg + 6] = data; + chr1_6(m_reg[6] | (m_reg[14] << 8), CHRROM); + break; + case 0x400b: + case 0x400f: + m_reg[reg + 7] = data; + chr1_7(m_reg[7] | (m_reg[15] << 8), CHRROM); + break; + case 0x4010: + prg16_89ab(data); + break; + case 0x4014: + if (data & 1) + set_nt_mirroring(PPU_MIRROR_HORZ); + else + set_nt_mirroring(PPU_MIRROR_VERT); + break; + } +} + +/*------------------------------------------------- + + Subor bootleg board Type 0 + + iNES: mapper 167 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_subor0_device::write_h) +{ + UINT8 subor_helper1, subor_helper2; + LOG_MMC(("subor0 write_h, offset: %04x, data: %02x\n", offset, data)); + + m_reg[(offset >> 13) & 0x03] = data; + subor_helper1 = ((m_reg[0] ^ m_reg[1]) << 1) & 0x20; + subor_helper2 = ((m_reg[2] ^ m_reg[3]) << 0) & 0x1f; + + if (m_reg[1] & 0x08) + { + subor_helper1 += subor_helper2 & 0xfe; + subor_helper2 = subor_helper1; + subor_helper1 += 1; + } + else if (m_reg[1] & 0x04) + { + subor_helper2 += subor_helper1; + subor_helper1 = 0x1f; + } + else + { + subor_helper1 += subor_helper2; + subor_helper2 = 0x20; + } + + prg16_89ab(subor_helper1); + prg16_cdef(subor_helper2); +} + +/*------------------------------------------------- + + Subor bootleg board Type 1 + + iNES: mapper 166 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_subor1_device::write_h) +{ + UINT8 subor_helper1, subor_helper2; + LOG_MMC(("subor1 write_h, offset: %04x, data: %02x\n", offset, data)); + + m_reg[(offset >> 13) & 0x03] = data; + subor_helper1 = ((m_reg[0] ^ m_reg[1]) << 1) & 0x20; + subor_helper2 = ((m_reg[2] ^ m_reg[3]) << 0) & 0x1f; + + if (m_reg[1] & 0x08) + { + subor_helper1 += subor_helper2 & 0xfe; + subor_helper2 = subor_helper1; + subor_helper2 += 1; + } + else if (m_reg[1] & 0x04) + { + subor_helper2 += subor_helper1; + subor_helper1 = 0x1f; + } + else + { + subor_helper1 += subor_helper2; + subor_helper2 = 0x07; + } + + prg16_89ab(subor_helper1); + prg16_cdef(subor_helper2); +} + +/*------------------------------------------------- + + Board UNL-CC-21 + + Games: Mi Hun Che + + In MESS: Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_cc21_device::write_h) +{ + LOG_MMC(("cc21 write_h, offset: %04x, data: %02x\n", offset, data)); + + set_nt_mirroring(BIT(offset, 1) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW); + chr8((offset & 0x01), CHRROM); +} + +/*------------------------------------------------- + + Bootleg Board for Xiao Zhuan Yuan + + Games: Shu Qi Yu - Zhi Li Xiao Zhuan Yuan + + Very simple mapper: writes to 0x5ff1 set prg32 (to data>>1), + while writes to 0x5ff2 set chr8 + + iNES: mapper 176 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_xiaozy_device::write_l) +{ + LOG_MMC(("xiaozy write_l, offset: %04x, data: %02x\n", offset, data)); + + switch (offset) + { + case 0x1ef1: /* 0x5ff1 */ + prg32(data >> 1); + break; + case 0x1ef2: /* 0x5ff2 */ + chr8(data, CHRROM); + break; + } +} + +/*------------------------------------------------- + + UNL-EDU2000 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_edu2k_device::write_h) +{ + LOG_MMC(("edu2k write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(data & 0x1f); + m_latch = (data & 0xc0) >> 6; +} + +WRITE8_MEMBER(nes_edu2k_device::write_m) +{ + LOG_MMC(("edu2k write_m, offset: %04x, data: %02x\n", offset, data)); + m_prgram[((m_latch * 0x2000) + offset) & (m_prgram.size() - 1)] = data; +} + +READ8_MEMBER(nes_edu2k_device::read_m) +{ + LOG_MMC(("edu2k read_m, offset: %04x\n", offset)); + return m_prgram[((m_latch * 0x2000) + offset) & (m_prgram.size() - 1)]; +} + +/*------------------------------------------------- + + Board UNL-T-230 + + Games: Dragon Ball Z IV (Unl) + + This mapper appears to be similar to Konami VRC-2 + but the game has no VROM and only 1 VRAM bank, so we + completely skip the chr bankswitch. If other games + using the same board and using CHR should surface, + we need to investigate this... + + In MESS: Supported + + -------------------------------------------------*/ + +// Identical to Konami IRQ +void nes_t230_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; + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + } +} + +WRITE8_MEMBER(nes_t230_device::write_h) +{ + LOG_MMC(("t230 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x700c) + { + case 0x0000: + break; + case 0x2000: + prg16_89ab(data); + break; + case 0x1000: + case 0x1004: + case 0x1008: + case 0x100c: + 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 0x7000: + m_irq_count_latch &= ~0x0f; + m_irq_count_latch |= data & 0x0f; + break; + case 0x7004: + m_irq_count_latch &= ~0xf0; + m_irq_count_latch |= (data << 4) & 0xf0; + break; + case 0x7008: + 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; + + default: + logerror("unl_t230_w uncaught offset: %04x value: %02x\n", offset, data); + break; + } +} + +/*------------------------------------------------- + + Bootleg Board for MK2 + + Games: Mortal Kombat II, Street Fighter III, Super Mario + Kart Rider + + This board uses an IRQ system very similar to MMC3. We indeed + use mapper4_irq, but there is some small glitch! + + iNES: mapper 91 + + In MESS: Supported. + + -------------------------------------------------*/ + +// Same IRQ as MMC3 +void nes_mk2_device::hblank_irq( int scanline, int vblank, int blanked ) +{ + if (scanline < PPU_BOTTOM_VISIBLE_SCANLINE) + { + int prior_count = m_irq_count; + if ((m_irq_count == 0) || m_irq_clear) + m_irq_count = m_irq_count_latch; + else + m_irq_count--; + + if (m_irq_enable && !blanked && (m_irq_count == 0) && (prior_count || m_irq_clear)) + { + LOG_MMC(("irq fired, scanline: %d (MAME %d, beam pos: %d)\n", scanline, + machine().first_screen()->vpos(), machine().first_screen()->hpos())); + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + } + } + m_irq_clear = 0; +} + +WRITE8_MEMBER(nes_mk2_device::write_m) +{ + LOG_MMC(("mk2 write_m, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x1000) + { + case 0x0000: + switch (offset & 0x03) + { + case 0x00: chr2_0(data, CHRROM); break; + case 0x01: chr2_2(data, CHRROM); break; + case 0x02: chr2_4(data, CHRROM); break; + case 0x03: chr2_6(data, CHRROM); break; + } + break; + case 0x1000: + switch (offset & 0x03) + { + case 0x00: prg8_89(data); break; + case 0x01: prg8_ab(data); break; + case 0x02: m_irq_enable = 0; m_irq_count = 0; break; + case 0x03: m_irq_enable = 1; m_irq_count = 7; break; + } + break; + default: + logerror("mk2 write_m, uncaught addr: %04x value: %02x\n", offset + 0x6000, data); + break; + } +} + + +/*------------------------------------------------- + + UNL-WOLRDHERO board emulation + + + iNES: + + -------------------------------------------------*/ + +// Same as Konami VRC IRQ... +void nes_whero_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; + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + } +} + +WRITE8_MEMBER(nes_whero_device::write_h) +{ + int bank, shift, mask1, mask2; + LOG_MMC(("World Hero write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x70c3) + { + case 0x0000: + if (!m_reg) + prg8_89(data); + else + prg8_cd(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 0x1002: + case 0x1080: + if (m_reg != (data & 2)) + { + m_reg = data & 2; + // swap banks! + prg8_89(m_prg_bank[2]); + prg8_cd(m_prg_bank[0]); + } + break; + + case 0x2000: + prg8_ab(data); + break; + + case 0x3000: case 0x3001: case 0x3002: case 0x3003: + case 0x4000: case 0x4001: case 0x4002: case 0x4003: + case 0x5000: case 0x5001: case 0x5002: case 0x5003: + case 0x6000: case 0x6001: case 0x6002: case 0x6003: + bank = ((offset & 0x7000) - 0x3000) / 0x0800 + BIT(offset, 1); + shift = (offset & 1) << 2; + mask1 = shift ? 0x00f : 0xff0; + mask2 = shift ? 0xff0 : 0x00f; + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & mask1) | ((data << shift) & mask2); + chr1_x(bank, m_mmc_vrom_bank[bank], CHRROM); + break; + + case 0x7000: + m_irq_count_latch = (m_irq_count_latch & 0xf0) | (data & 0x0f); + break; + + case 0x7001: + m_irq_count_latch = (m_irq_count_latch & 0x0f) | ((data & 0x0f) << 4); + break; + + case 0x7002: + // 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 0x7003: + m_irq_enable = m_irq_enable_latch; + break; + } +} + + +/*------------------------------------------------- + + UNL-43272 + + Games: Gaau Hok Gwong Cheung + + In MESS: Preliminary Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_43272_device::write_h) +{ + LOG_MMC(("unl_43272 write_h, offset: %04x, data: %02x\n", offset, data)); + + if ((m_latch & 0x81) == 0x81) + prg32((m_latch & 0x38) >> 3); + + m_latch = offset & 0xffff; +} + + +READ8_MEMBER(nes_43272_device::read_h) +{ + UINT8 mask = (m_latch & 0x400) ? 0xfe : 0xff; + LOG_MMC(("unl_43272 read_h, offset: %04x\n", offset)); + + return hi_access_rom(offset & mask); +} + + +/*------------------------------------------------- + + UNL-TF1201 + + Games: + + In MESS: Preliminary Supported + + -------------------------------------------------*/ + +void nes_tf1201_device::hblank_irq(int scanline, int vblank, int blanked) +{ + if (m_irq_enable_latch != m_irq_enable && scanline < 240) + m_irq_count -= 8; + + if (m_irq_enable) + { + m_irq_count++; + if ((m_irq_count & 0xff) == 238) + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + } +} + +void nes_tf1201_device::update_prg() +{ + prg8_89(m_swap ? 0xff : m_prg); + prg8_cd(m_swap ? m_prg : 0xff ); +} + +WRITE8_MEMBER(nes_tf1201_device::write_h) +{ + int bank; + LOG_MMC(("unl_tf1201 write_h, offset: %04x, data: %02x\n", offset, data)); + + offset = (offset & 0x7003) | ((offset & 0x0c) >> 2); // nestopia does not OR here... + + switch (offset & 0x7003) + { + case 0x0000: + m_prg = data; + update_prg(); + break; + case 0x1000: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 0x1001: + m_swap = data & 0x03; + update_prg(); + break; + case 0x2000: + prg8_ab(data); + break; + case 0x3000: case 0x3001: case 0x3002: case 0x3003: + case 0x4000: case 0x4001: case 0x4002: case 0x4003: + case 0x5000: case 0x5001: case 0x5002: case 0x5003: + case 0x6000: case 0x6001: case 0x6002: case 0x6003: + bank = (((offset - 0x3000) >> 11) | (offset & 0x1)) & 0x7; + if (offset & 2) + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4); + else + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | ((data & 0x0f) << 0); + chr1_x(bank, m_mmc_vrom_bank[bank], m_chr_source); + break; + case 0x7000: + m_irq_count = (m_irq_count & 0xf0) | (data & 0x0f); + break; + case 0x7001: + case 0x7003: + m_irq_enable_latch = m_irq_enable; + m_irq_enable = BIT(data, 1); + break; + case 0x7002: + m_irq_count = (m_irq_count & 0x0f) | ((data & 0x0f) << 4); + break; + } +} + + +/*------------------------------------------------- + + UNL-CITYFIGHT + + Games: + + Additional audio register not emulated yet! + + In MESS: Preliminary Supported + + -------------------------------------------------*/ + +void nes_cityfight_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (!m_irq_count) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + m_irq_count = 0xffff; + } + else + m_irq_count--; + } + } +} + +void nes_cityfight_device::update_prg() +{ + prg32(m_prg_reg >> 2); + if (!m_prg_mode) + prg8_cd(m_prg_reg); +} + +WRITE8_MEMBER(nes_cityfight_device::write_h) +{ + int bank; + LOG_MMC(("unl_cityfight write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x700c) + { + 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; + } + m_prg_reg = data & 0xc; + break; + case 0x1004: + case 0x1008: + case 0x100c: + if (offset & 0x800) + LOG_MMC(("Extended Audio write, data %x!", data & 0x0f));//pcmwrite(0x4011, (V & 0xf) << 3); + else + m_prg_reg = data & 0xc; + break; + + + case 0x2000: case 0x2004: case 0x2008: case 0x200c: + bank = 2 + BIT(offset, 3); + if (offset & 4) + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4); + else + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | ((data & 0x0f) << 0); + chr1_x(bank, m_mmc_vrom_bank[bank], m_chr_source); + break; + case 0x3000: case 0x3004: case 0x3008: case 0x300c: + bank = 4 + BIT(offset, 3); + if (offset & 4) + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4); + else + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | ((data & 0x0f) << 0); + chr1_x(bank, m_mmc_vrom_bank[bank], m_chr_source); + break; + case 0x5000: case 0x5004: case 0x5008: case 0x500c: + bank = 0 + BIT(offset, 3); + if (offset & 4) + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4); + else + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | ((data & 0x0f) << 0); + chr1_x(bank, m_mmc_vrom_bank[bank], m_chr_source); + break; + case 0x6000: case 0x6004: case 0x6008: case 0x600c: + bank = 6 + BIT(offset, 3); + if (offset & 4) + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4); + else + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | ((data & 0x0f) << 0); + chr1_x(bank, m_mmc_vrom_bank[bank], m_chr_source); + break; + + case 0x4000: + case 0x4004: + case 0x4008: + case 0x400c: + m_prg_mode = data & 1; + + case 0x7000: + m_irq_count = (m_irq_count & 0x1e0) | ((data & 0x0f) << 1); + break; + case 0x7004: + m_irq_count = (m_irq_count & 0x1e) | ((data & 0x0f) << 5); + break; + case 0x7008: + m_irq_enable = BIT(data, 1); + break; + } +} + + +#ifdef UNUSED_FUNCTION +/*------------------------------------------------- + + FUJIYA Board - mapper 170 according to NEStopia + + Is this possibly for the Fujiya Famikase series + of educational titles? Is there any dump around? + + -------------------------------------------------*/ + +void nes_fujiya_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_fujiya_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0); + chr8(0, m_chr_source); + + m_latch = 0; +} + +WRITE8_MEMBER(nes_fujiya_device::write_m) +{ + LOG_MMC(("fujiya write_m, offset: %04x, data: %02x\n", offset, data)); + offset += 0x6000; + + if (offset == 0x6502 || offset == 0x7000) + m_latch = (data & 0x40) << 1; +} + +READ8_MEMBER(nes_fujiya_device::read_m) +{ + LOG_MMC(("fujiya read_m, offset: %04x\n", offset)); + offset += 0x6000; + + if (offset == 0x7001 || offset == 0x7777) + return m_latch | ((offset >> 8) & 0x7f); + + return m_open_bus; // open bus +} +#endif diff --git a/src/devices/bus/nes/pirate.h b/src/devices/bus/nes/pirate.h new file mode 100644 index 00000000000..f11c53ced0d --- /dev/null +++ b/src/devices/bus/nes/pirate.h @@ -0,0 +1,399 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_PIR_H +#define __NES_PIR_H + +#include "nxrom.h" + + +// ======================> nes_agci_device + +class nes_agci_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_agci_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_dreamtech_device + +class nes_dreamtech_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_dreamtech_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); +}; + + +// ======================> nes_fukutake_device + +class nes_fukutake_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_fukutake_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; + UINT8 m_ram[0xb00]; +}; + + +// ======================> nes_futuremedia_device + +class nes_futuremedia_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_futuremedia_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +private: + UINT16 m_irq_count, m_irq_count_latch; + UINT8 m_irq_clear; + int m_irq_enable; +}; + + +// ======================> nes_magseries_device + +class nes_magseries_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_magseries_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_daou306_device + +class nes_daou306_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_daou306_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[16]; +}; + + +// ======================> nes_subor0_device + +class nes_subor0_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_subor0_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[4]; +}; + + +// ======================> nes_subor1_device + +class nes_subor1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_subor1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_reg[4]; +}; + + +// ======================> nes_cc21_device + +class nes_cc21_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_cc21_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_xiaozy_device + +class nes_xiaozy_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_xiaozy_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); +}; + + +// ======================> nes_edu2k_device + +class nes_edu2k_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_edu2k_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_t230_device + +class nes_t230_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_t230_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +private: + UINT16 m_irq_count, m_irq_count_latch; + UINT8 m_irq_mode; + int m_irq_enable, m_irq_enable_latch; + + UINT8 m_mmc_vrom_bank[8]; +}; + + +// ======================> nes_mk2_device + +class nes_mk2_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_mk2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +private: + UINT16 m_irq_count, m_irq_count_latch; + UINT8 m_irq_clear; + int m_irq_enable; +}; + + +// ======================> nes_whero_device + +class nes_whero_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_whero_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +private: + UINT8 m_reg; + UINT8 m_mmc_vrom_bank[8]; + + UINT16 m_irq_count, m_irq_count_latch; + int m_irq_enable, m_irq_enable_latch; +// int m_irq_mode; +}; + + +// ======================> nes_43272_device + +class nes_43272_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_43272_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT16 m_latch; +}; + + +// ======================> nes_tf1201_device + +class nes_tf1201_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_tf1201_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +private: + void update_prg(); + UINT8 m_prg, m_swap; + UINT16 m_irq_count; + int m_irq_enable, m_irq_enable_latch; + + UINT8 m_mmc_vrom_bank[8]; +}; + + +// ======================> nes_cityfight_device + +class nes_cityfight_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_cityfight_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + void update_prg(); + UINT8 m_prg_reg, m_prg_mode; + UINT16 m_irq_count; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; + + UINT8 m_mmc_vrom_bank[8]; +}; + + + +#ifdef UNUSED_FUNCTION +// ======================> nes_fujiya_device + +class nes_fujiya_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_fujiya_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; +#endif + + +// device type definition +extern const device_type NES_AGCI_50282; +extern const device_type NES_DREAMTECH01; +extern const device_type NES_FUKUTAKE; +extern const device_type NES_FUTUREMEDIA; +extern const device_type NES_MAGSERIES; +extern const device_type NES_DAOU306; +extern const device_type NES_SUBOR0; +extern const device_type NES_SUBOR1; +extern const device_type NES_CC21; +extern const device_type NES_XIAOZY; +extern const device_type NES_EDU2K; +extern const device_type NES_T230; +extern const device_type NES_MK2; +extern const device_type NES_WHERO; +extern const device_type NES_43272; +extern const device_type NES_TF1201; +extern const device_type NES_CITYFIGHT; +extern const device_type NES_FUJIYA; + +#endif diff --git a/src/devices/bus/nes/pt554.c b/src/devices/bus/nes/pt554.c new file mode 100644 index 00000000000..4f6e843d8d1 --- /dev/null +++ b/src/devices/bus/nes/pt554.c @@ -0,0 +1,107 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Bandai PT-554 PCBs + + + Here we emulate the following Bandai PT-554 PCB (a CNROM PCB + LPC / PARCOR speech synthesis chip) + + TODO: + - emulate the mat controller + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "pt554.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_BANDAI_PT554 = &device_creator; + + +nes_bandai_pt554_device::nes_bandai_pt554_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_cnrom_device(mconfig, NES_BANDAI_PT554, "NES Cart Bandai PT-554 PCB", tag, owner, clock, "nes_bandai_pt554", __FILE__), + m_samples(*this, "samples") +{ +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Bandai PT-554 board emulation + + This is used by Aerobics Studio. It is basically a CNROM board + with an Mitsubishi M50805 LPC / PARCOR speech synthesis chip + with internal tables stored in ROM which have not yet been dumped. + + iNES: mapper 3? + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_bandai_pt554_device::write_m) +{ + LOG_MMC(("Bandai PT-554 Sound write, data: %02x\n", data)); + + if (!BIT(data, 6)) + m_samples->start(data & 0x07, data & 0x07); + else + m_samples->stop(data & 0x07); +} + +static const char *const pt554_sample_names[] = +{ + "*ftaerobi", + "00", + "01", + "02", + "03", + "04", + "05", + "06", + "07", + 0 +}; + +//------------------------------------------------- +// MACHINE_DRIVER +//------------------------------------------------- + +static MACHINE_CONFIG_FRAGMENT( pt554 ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("samples", SAMPLES, 0) + MCFG_SAMPLES_CHANNELS(8) + MCFG_SAMPLES_NAMES(pt554_sample_names) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.50) +MACHINE_CONFIG_END + +//------------------------------------------------- +// machine_config_additions +//------------------------------------------------- + +machine_config_constructor nes_bandai_pt554_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( pt554 ); +} diff --git a/src/devices/bus/nes/pt554.h b/src/devices/bus/nes/pt554.h new file mode 100644 index 00000000000..0d0bd7b60f4 --- /dev/null +++ b/src/devices/bus/nes/pt554.h @@ -0,0 +1,31 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_PT554_H +#define __NES_PT554_H + +#include "nxrom.h" + + +// ======================> nes_bandai_pt554_device + +class nes_bandai_pt554_device : public nes_cnrom_device +{ +public: + // construction/destruction + nes_bandai_pt554_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual machine_config_constructor device_mconfig_additions() const; + virtual DECLARE_WRITE8_MEMBER(write_m); + +private: + required_device m_samples; +}; + + + + +// device type definition +extern const device_type NES_BANDAI_PT554; + +#endif diff --git a/src/devices/bus/nes/racermate.c b/src/devices/bus/nes/racermate.c new file mode 100644 index 00000000000..6989c2254cb --- /dev/null +++ b/src/devices/bus/nes/racermate.c @@ -0,0 +1,92 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for RacerMate PCBs + + + Here we emulate the UNL-RACERMATE PCB [mapper 168] + + TODO: + - save VRAM + - emulate the bike controller? + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "racermate.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_RACERMATE = &device_creator; + + +nes_racermate_device::nes_racermate_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_RACERMATE, "NES Cart Racermate PCB", tag, owner, clock, "nes_racermate", __FILE__) +{ +} + + + +void nes_racermate_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_racermate_device::pcb_reset() +{ +// m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(m_prg_chunks - 1); + chr4_0(0, CHRRAM); + chr4_4(0, CHRRAM); + + m_latch = 0; +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Board UNL-RACERMATE + + In MESS: *VERY* preliminary support. Also, it seems that this + board saves to battery the CHRRAM!!! + + -------------------------------------------------*/ + +void nes_racermate_device::update_banks() +{ + chr4_4(m_latch & 0x0f, m_chr_source); + prg16_89ab(m_latch >> 6); +} + +WRITE8_MEMBER(nes_racermate_device::write_h) +{ + LOG_MMC(("racermate write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset == 0x3000) + { + m_latch = data; + update_banks(); + } +} diff --git a/src/devices/bus/nes/racermate.h b/src/devices/bus/nes/racermate.h new file mode 100644 index 00000000000..209c2f56601 --- /dev/null +++ b/src/devices/bus/nes/racermate.h @@ -0,0 +1,35 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_RACERMATE_H +#define __NES_RACERMATE_H + +#include "nxrom.h" + + +// ======================> nes_racermate_device + +class nes_racermate_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_racermate_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + void update_banks(); + UINT8 m_latch; +}; + + + + + +// device type definition +extern const device_type NES_RACERMATE; + +#endif diff --git a/src/devices/bus/nes/rcm.c b/src/devices/bus/nes/rcm.c new file mode 100644 index 00000000000..28ec6e8277e --- /dev/null +++ b/src/devices/bus/nes/rcm.c @@ -0,0 +1,303 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for RCM PCBs + + + Here we emulate the following PCBs + + * RCM GS2015 [mapper 216] + * RCM GS2004 + * RCM GS2013 + * RCM Tetris Family 9in1 [mapper 61] + * RCM 3D Block + + TODO: + - investigate why 3D Block does not work + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "rcm.h" + +#include "cpu/m6502/m6502.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_GS2015 = &device_creator; +const device_type NES_GS2004 = &device_creator; +const device_type NES_GS2013 = &device_creator; +const device_type NES_TF9IN1 = &device_creator; +const device_type NES_3DBLOCK = &device_creator; + + +nes_gs2015_device::nes_gs2015_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_GS2015, "NES Cart RCM GS-2015 PCB", tag, owner, clock, "nes_gs2015", __FILE__) +{ +} + +nes_gs2004_device::nes_gs2004_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_GS2004, "NES Cart RCM GS-2004 PCB", tag, owner, clock, "nes_gs2004", __FILE__) +{ +} + +nes_gs2013_device::nes_gs2013_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_GS2013, "NES Cart RCM GS-2013 PCB", tag, owner, clock, "nes_gs2013", __FILE__) +{ +} + +nes_tf9_device::nes_tf9_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_TF9IN1, "NES Cart RCM Tetris Family 9 in 1 PCB", tag, owner, clock, "nes_tetrisfam", __FILE__) +{ +} + +nes_3dblock_device::nes_3dblock_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_3DBLOCK, "NES Cart RCM 3D Block PCB", tag, owner, clock, "nes_3dblock", __FILE__) +{ +} + + + + +void nes_gs2015_device::device_start() +{ + common_start(); +} + +void nes_gs2015_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_gs2004_device::device_start() +{ + common_start(); +} + +void nes_gs2004_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0xff); + chr8(0, m_chr_source); +} + +void nes_gs2013_device::device_start() +{ + common_start(); +} + +void nes_gs2013_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0xff); + chr8(0, m_chr_source); +} + +void nes_tf9_device::device_start() +{ + common_start(); +} + +void nes_tf9_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_3dblock_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); + save_item(NAME(m_irq_count)); +} + +void nes_3dblock_device::pcb_reset() +{ + prg32(0); + chr8(0, CHRRAM); + m_reg[0] = 0; + m_reg[1] = 0; + m_reg[2] = 0; + m_reg[3] = 0; + m_irq_count = 0; +} + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + RCM GS2015 Board + + Games: Bonza, Magic Jewelry 2 + + Very simple mapper: writes to 0x8000-0xffff sets prg32 + to offset and chr8 to offset>>1 (when chrrom is present) + + iNES: mapper 216 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_gs2015_device::write_h) +{ + LOG_MMC(("gs2015 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(offset); + chr8(offset >> 1, m_chr_source); +} + +READ8_MEMBER(nes_gs2015_device::read_m) +{ + LOG_MMC(("gs2015 read_m, offset: %04x\n", offset)); + return 0; // Videopoker Bonza needs this (sort of protection? or related to inputs?) +} + +/*------------------------------------------------- + + Board BMC-GS2004 + + Games: Tetris Family 6-in-1 + + In MESS: Preliminary Support. It also misses WRAM handling + (we need reads from 0x6000-0x7fff) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_gs2004_device::write_h) +{ + LOG_MMC(("gs2004 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(data); +} + +/*------------------------------------------------- + + Board BMC-GS2013 + + Games: Tetris Family 12-in-1 + + In MESS: Preliminary Support. It also misses WRAM handling + (we need reads from 0x6000-0x7fff) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_gs2013_device::write_h) +{ + LOG_MMC(("gs2013 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (data & 0x08) + prg32(data & 0x09); + else + prg32(data & 0x07); +} + +/*------------------------------------------------- + + Bootleg Board by RCM for Tetris Family + + Games: Tetris Family 9 in 1, 20 in 1 + + Simple Mapper: prg/chr/nt are swapped depending on the offset + of writes in 0x8000-0xffff. offset&0x80 set NT mirroring, + when (offset&0x30) is 0,3 prg32 is set; when it is 1,2 + two 16k prg banks are set. See below for the values used in + these banks. + + iNES: mapper 61 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_tf9_device::write_h) +{ + LOG_MMC(("tetrisfam write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x30) + { + case 0x00: + case 0x30: + prg32(offset & 0x0f); + break; + case 0x10: + case 0x20: + prg16_89ab(((offset & 0x0f) << 1) | ((offset & 0x20) >> 4)); + prg16_cdef(((offset & 0x0f) << 1) | ((offset & 0x20) >> 4)); + break; + } + set_nt_mirroring(BIT(data, 7) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); +} + +/*------------------------------------------------- + + Bootleg Board by RCM for 3-D Block + + Games: 3-D Block Hwang Shinwei version + + iNES: + + In MESS: Very Preliminary Support. What is the purpose + of the writes to $4800-$4900-$4a00? These writes + also happens on the RCM version, which however works + (probably an unused leftover code in that version) + + FCEUmm suggests it might be IRQ related, but + it does not seem to help much... + + -------------------------------------------------*/ + +void nes_3dblock_device::hblank_irq(int scanline, int vblank, int blanked) +{ + if (m_irq_count) + { + m_irq_count--; + if (!m_irq_count) + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + } +} + +WRITE8_MEMBER(nes_3dblock_device::write_l) +{ + LOG_MMC(("3dblock write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + switch (offset) + { + case 0x800: // $4800 + m_reg[0] = data; + break; + case 0x900: // $4900 + m_reg[1] = data; + break; + case 0xa00: // $4a00 + m_reg[2] = data; + break; + case 0xe00: // $4e00 + m_reg[3] = data; m_irq_count = 0x10; + break; + } +} diff --git a/src/devices/bus/nes/rcm.h b/src/devices/bus/nes/rcm.h new file mode 100644 index 00000000000..4a5c05b6aa9 --- /dev/null +++ b/src/devices/bus/nes/rcm.h @@ -0,0 +1,105 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_RCM_H +#define __NES_RCM_H + +#include "nxrom.h" + + +// ======================> nes_gs2015_device + +class nes_gs2015_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_gs2015_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l) { return read_m(space, offset, mem_mask); } + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_gs2004_device + +class nes_gs2004_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_gs2004_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_gs2013_device + +class nes_gs2013_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_gs2013_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_tf9_device + +class nes_tf9_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_tf9_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_3dblock_device + +class nes_3dblock_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_3dblock_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +private: + UINT8 m_reg[4]; + UINT8 m_irq_count; +}; + + + + +// device type definition +extern const device_type NES_GS2015; +extern const device_type NES_GS2004; +extern const device_type NES_GS2013; +extern const device_type NES_TF9IN1; +extern const device_type NES_3DBLOCK; + +#endif diff --git a/src/devices/bus/nes/rexsoft.c b/src/devices/bus/nes/rexsoft.c new file mode 100644 index 00000000000..c0e261dc5fa --- /dev/null +++ b/src/devices/bus/nes/rexsoft.c @@ -0,0 +1,262 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Rex Soft PCBs + + + Here we emulate the following PCBs + + * Rex Soft DragonBall Z V [mapper 12] + * Rex Soft SL-1632 [mapper 14] + + TODO: + - fix 0x6000-0x7fff accesses, write_m/read_m + - check glitches in SL-1632 + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "rexsoft.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_REX_DBZ5 = &device_creator; +const device_type NES_REX_SL1632 = &device_creator; + + +nes_rex_dbz5_device::nes_rex_dbz5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_REX_DBZ5, "NES Cart Rex Soft DragonBall Z V PCB", tag, owner, clock, "nes_rex_dbz5", __FILE__) +{ +} + +nes_rex_sl1632_device::nes_rex_sl1632_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_REX_SL1632, "NES Cart Rex Soft SL-1632 PCB", tag, owner, clock, "nes_rex_sl1632", __FILE__) +{ +} + + + + +void nes_rex_dbz5_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_extra)); +} + +void nes_rex_dbz5_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + m_extra = 0; + mmc3_common_initialize(0xff, 0xff, 0); +} + +void nes_rex_sl1632_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_mode)); + save_item(NAME(m_mirror)); + save_item(NAME(m_extra_bank)); +} + +void nes_rex_sl1632_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_mode = 0; + m_mirror = 0; + memset(m_extra_bank, 0, sizeof(m_extra_bank)); + m_extra_bank[2] = 0xfe; + m_extra_bank[3] = 0xff; + mmc3_common_initialize(0xff, 0xff, 0); +} + + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Bootleg Board by Rex Soft + + Games: Dragon Ball Z 5, Dragon Ball Z Super + + MMC3 clone. Writes to 0x4100-0x5fff (or from 0x4020?) + possibly select higher VROM pages (to allow up to + 512 banks instead of 256 only): bit0 selects the + higher pages for PPU banks 0-3 (0x0000-0x0fff), while + bit4 selects the higher pages for PPU banks 4-7 (0x1000-0x1fff) + + iNES: mapper 12 + + In MESS: Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_rex_dbz5_device::write_l) +{ + LOG_MMC(("rex_dbz write_l, offset: %04x, data: %02x\n", offset, data)); + + m_extra = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); +} + +/* we would need to use this read handler in 0x6000-0x7fff as well */ +READ8_MEMBER(nes_rex_dbz5_device::read_l) +{ + LOG_MMC(("rex_dbz read_l, offset: %04x\n", offset)); + return 0x01; +} + +void nes_rex_dbz5_device::chr_cb(int start, int bank, int source) +{ + int shift = (start < 4) ? 8 : 4; + + bank |= ((m_extra << shift) & 0x100); + chr1_x(start, bank, source); +} + +/*------------------------------------------------- + + Rex Soft SL1632 Board + + Games: Samurai Spirits + + MMC3 clone + + iNES: mapper 14 + + In MESS: Supported + + -------------------------------------------------*/ + +void nes_rex_sl1632_device::set_prg(int prg_base, int prg_mask) +{ + if (m_mode & 0x02) + { + // here standard MMC3 bankswitch + UINT8 prg_flip = (m_latch & 0x40) ? 2 : 0; + + prg_cb(0, prg_base | (m_mmc_prg_bank[0 ^ prg_flip] & prg_mask)); + prg_cb(1, prg_base | (m_mmc_prg_bank[1] & prg_mask)); + prg_cb(2, prg_base | (m_mmc_prg_bank[2 ^ prg_flip] & prg_mask)); + prg_cb(3, prg_base | (m_mmc_prg_bank[3] & prg_mask)); + } + else + { + prg8_89(m_extra_bank[0]); + prg8_ab(m_extra_bank[1]); + prg8_cd(m_extra_bank[2]); + prg8_ef(m_extra_bank[3]); + } +} + +void nes_rex_sl1632_device::set_chr(UINT8 chr, int chr_base, int chr_mask) +{ + static const UINT8 conv_table[8] = {5, 5, 5, 5, 3, 3, 1, 1}; + UINT8 chr_page = (m_latch & 0x80) >> 5; + UINT8 bank[8]; + UINT8 chr_base2[8]; + + if (m_mode & 0x02) + { + for (int i = 0; i < 8; i++) + { + // since the mapper acts on 1K CHR chunks, we have 8 banks which use the 6 m_mmc_vrom_bank from MMC3 base class + if (i < 4) + bank[i] = ((m_mmc_vrom_bank[i / 2] & 0xfe) | (i & 1)); + else + bank[i] = m_mmc_vrom_bank[i - 2]; + chr_base2[i] = chr_base | ((m_mode << conv_table[i]) & 0x100); + } + } + else + { + for (int i = 0; i < 8; i++) + { + bank[i] = m_extra_bank[i + 4]; // first 4 m_extra_banks are PRG + chr_base2[i] = chr_base; + } + } + + chr1_x(chr_page ^ 0, chr_base2[0] | (bank[0] & chr_mask), chr); + chr1_x(chr_page ^ 1, chr_base2[1] | (bank[1] & chr_mask), chr); + chr1_x(chr_page ^ 2, chr_base2[2] | (bank[2] & chr_mask), chr); + chr1_x(chr_page ^ 3, chr_base2[3] | (bank[3] & chr_mask), chr); + chr1_x(chr_page ^ 4, chr_base2[4] | (bank[4] & chr_mask), chr); + chr1_x(chr_page ^ 5, chr_base2[5] | (bank[5] & chr_mask), chr); + chr1_x(chr_page ^ 6, chr_base2[6] | (bank[6] & chr_mask), chr); + chr1_x(chr_page ^ 7, chr_base2[7] | (bank[7] & chr_mask), chr); +} + +WRITE8_MEMBER(nes_rex_sl1632_device::write_h) +{ + UINT8 helper1, helper2; + LOG_MMC(("rex_sl1632 write_h, offset: %04x, data: %02x\n", offset, data)); + + if (offset == 0x2131) + { + m_mode = data; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); + + if (!(m_mode & 0x02)) + set_nt_mirroring(BIT(m_mirror, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + } + + if (m_mode & 0x02) + { + switch (offset & 0x6001) + { + case 0x2000: + set_nt_mirroring(BIT(m_mirror, 0) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ); + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } + } + else if (offset >= 0x3000 && offset <= 0x6003) + { + helper1 = (offset & 0x01) << 2; + offset = ((offset & 0x02) | (offset >> 10)) >> 1; + helper2 = ((offset + 2) & 0x07) + 4; // '+4' because first 4 m_extra_banks are for PRG! + m_extra_bank[helper2] = (m_extra_bank[helper2] & (0xf0 >> helper1)) | ((data & 0x0f) << helper1); + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } + else + { + switch (offset & 0x7003) + { + case 0x0000: + case 0x2000: + m_extra_bank[offset >> 13] = data; + set_prg(m_prg_base, m_prg_mask); + break; + + case 0x1000: + m_mirror = data; + set_nt_mirroring(BIT(m_mirror, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + } + } +} diff --git a/src/devices/bus/nes/rexsoft.h b/src/devices/bus/nes/rexsoft.h new file mode 100644 index 00000000000..81527759574 --- /dev/null +++ b/src/devices/bus/nes/rexsoft.h @@ -0,0 +1,61 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_REXSOFT_H +#define __NES_REXSOFT_H + +#include "mmc3.h" + + +// ======================> nes_rex_dbz5_device + +class nes_rex_dbz5_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_rex_dbz5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_READ8_MEMBER(read_m) { return read_l(space, offset, mem_mask); } + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual void chr_cb( int start, int bank, int source ); + + virtual void pcb_reset(); + +private: + UINT8 m_extra; +}; + + +// ======================> nes_rex_sl1632_device + +class nes_rex_sl1632_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_rex_sl1632_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + virtual void set_prg(int prg_base, int prg_mask); + virtual void set_chr(UINT8 chr, int chr_base, int chr_mask); + + UINT8 m_mode, m_mirror; + UINT8 m_extra_bank[12]; +}; + + + + + +// device type definition +extern const device_type NES_REX_DBZ5; +extern const device_type NES_REX_SL1632; + +#endif diff --git a/src/devices/bus/nes/sachen.c b/src/devices/bus/nes/sachen.c new file mode 100644 index 00000000000..87edbc6a700 --- /dev/null +++ b/src/devices/bus/nes/sachen.c @@ -0,0 +1,695 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Sachen PCBs + + + Here we emulate the following PCBs + + * Sachen SA-009 [mapper 160] + * Sachen SA-0036 [mapper 148] + * Sachen SA-0037 [mapper 149] + * Sachen SA-72007 [mapper 145] + * Sachen SA-72008 [mapper 133] + * Sachen TCA-01 [mapper 143] + * Sachen TCU-01 [mapper 147] + * Sachen TCU-02 [mapper 136] + * Sachen Discrete PCBs [mapper 150 & 243] + * Sachen 8259 [mapper 141 (A), 138 (B), 139 (C), 137 (D)] + + Known issues on specific mappers: + + * 133 Qi Wang starts with corrupted graphics (ingame seems better) + + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "sachen.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_SACHEN_SA009 = &device_creator; +const device_type NES_SACHEN_SA0036 = &device_creator; +const device_type NES_SACHEN_SA0037 = &device_creator; +const device_type NES_SACHEN_SA72007 = &device_creator; +const device_type NES_SACHEN_SA72008 = &device_creator; +const device_type NES_SACHEN_TCA01 = &device_creator; +const device_type NES_SACHEN_TCU01 = &device_creator; +const device_type NES_SACHEN_TCU02 = &device_creator; +const device_type NES_SACHEN_74X374 = &device_creator; +const device_type NES_SACHEN_74X374_ALT = &device_creator; +const device_type NES_SACHEN_8259A = &device_creator; +const device_type NES_SACHEN_8259B = &device_creator; +const device_type NES_SACHEN_8259C = &device_creator; +const device_type NES_SACHEN_8259D = &device_creator; + + +nes_sachen_sa009_device::nes_sachen_sa009_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SACHEN_SA009, "NES Cart Sachen SA009 PCB", tag, owner, clock, "nes_sa009", __FILE__) +{ +} + +nes_sachen_sa0036_device::nes_sachen_sa0036_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SACHEN_SA0036, "NES Cart Sachen sa0036 PCB", tag, owner, clock, "nes_sa0036", __FILE__) +{ +} + +nes_sachen_sa0037_device::nes_sachen_sa0037_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SACHEN_SA0037, "NES Cart Sachen sa0037 PCB", tag, owner, clock, "nes_sa0037", __FILE__) +{ +} + +nes_sachen_sa72007_device::nes_sachen_sa72007_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SACHEN_SA72007, "NES Cart Sachen SA72007 PCB", tag, owner, clock, "nes_sa72007", __FILE__) +{ +} + +nes_sachen_sa72008_device::nes_sachen_sa72008_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SACHEN_SA72008, "NES Cart Sachen SA72008 PCB", tag, owner, clock, "nes_sa72008", __FILE__) +{ +} + +nes_sachen_tca01_device::nes_sachen_tca01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SACHEN_TCA01, "NES Cart Sachen TCA-01 PCB", tag, owner, clock, "nes_tca01", __FILE__) +{ +} + +nes_sachen_tcu01_device::nes_sachen_tcu01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SACHEN_TCU01, "NES Cart Sachen TCU-01 PCB", tag, owner, clock, "nes_tcu01", __FILE__) +{ +} + +nes_sachen_tcu02_device::nes_sachen_tcu02_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SACHEN_TCU02, "NES Cart Sachen TCU-02 PCB", tag, owner, clock, "nes_tcu02", __FILE__) +{ +} + +nes_sachen_74x374_device::nes_sachen_74x374_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_sachen_74x374_device::nes_sachen_74x374_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SACHEN_74X374, "NES Cart Sachen 74*374 PCB", tag, owner, clock, "nes_s74x34", __FILE__) +{ +} + +nes_sachen_74x374_alt_device::nes_sachen_74x374_alt_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sachen_74x374_device(mconfig, NES_SACHEN_74X374_ALT, "NES Cart Sachen 74*374 Alt PCB", tag, owner, clock, "nes_s74x34a", __FILE__) +{ +} + +nes_sachen_8259a_device::nes_sachen_8259a_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_sachen_74x374_device(mconfig, type, name, tag, owner, clock, shortname, source) +{ +} + +nes_sachen_8259a_device::nes_sachen_8259a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sachen_74x374_device(mconfig, NES_SACHEN_8259A, "NES Cart Sachen 8259A PCB", tag, owner, clock, "nes_s8259a", __FILE__) +{ +} + +nes_sachen_8259b_device::nes_sachen_8259b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sachen_8259a_device(mconfig, NES_SACHEN_8259B, "NES Cart Sachen 8259B PCB", tag, owner, clock, "nes_s8259b", __FILE__) +{ +} + +nes_sachen_8259c_device::nes_sachen_8259c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sachen_8259a_device(mconfig, NES_SACHEN_8259C, "NES Cart Sachen 8259C PCB", tag, owner, clock, "nes_s8259c", __FILE__) +{ +} + +nes_sachen_8259d_device::nes_sachen_8259d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sachen_8259a_device(mconfig, NES_SACHEN_8259D, "NES Cart Sachen 8259D PCB", tag, owner, clock, "nes_s8259d", __FILE__) +{ +} + + + + +void nes_sachen_sa009_device::device_start() +{ + common_start(); +} + +void nes_sachen_sa009_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_sachen_sa0036_device::device_start() +{ + common_start(); +} + +void nes_sachen_sa0036_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_sachen_sa0037_device::device_start() +{ + common_start(); +} + +void nes_sachen_sa0037_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_sachen_sa72007_device::device_start() +{ + common_start(); +} + +void nes_sachen_sa72007_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_sachen_sa72008_device::device_start() +{ + common_start(); +} + +void nes_sachen_sa72008_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_sachen_tca01_device::device_start() +{ + common_start(); +} + +void nes_sachen_tca01_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(1); + chr8(0, m_chr_source); +} + +void nes_sachen_tcu01_device::device_start() +{ + common_start(); +} + +void nes_sachen_tcu01_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_sachen_tcu02_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_sachen_tcu02_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_latch = 0; +} + +void nes_sachen_74x374_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + save_item(NAME(m_mmc_vrom_bank)); +} + +void nes_sachen_74x374_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_latch = 0; + m_mmc_vrom_bank = 0; +} + +void nes_sachen_8259a_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + save_item(NAME(m_reg)); +} + +void nes_sachen_8259a_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + set_nt_mirroring(PPU_MIRROR_VERT); + + m_latch = 0; + memset(m_reg, 0, sizeof(m_reg)); +} + +void nes_sachen_8259d_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(m_vrom_chunks - 1, CHRROM); + set_nt_mirroring(PPU_MIRROR_VERT); + + m_latch = 0; + memset(m_reg, 0, sizeof(m_reg)); +} + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Sachen SA009 bootleg boards + + Games: Pipe 5 + + iNES: mapper 160 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sachen_sa009_device::write_l) +{ + LOG_MMC(("SA009 write_l, offset: %04x, data: %02x\n", offset, data)); + + chr8(data, m_chr_source); +} + +/*------------------------------------------------- + + Sachen SA0036 bootleg boards + + Games: Taiwan Mahjong 16 + + iNES: mapper 149 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sachen_sa0036_device::write_h) +{ + LOG_MMC(("sa0036 write_h, offset: %04x, data: %02x\n", offset, data)); + + chr8(data >> 7, CHRROM); +} + +/*------------------------------------------------- + + Sachen SA0037 bootleg boards + + Games: Mahjong World, Shisen Mahjong + + iNES: mapper 148 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sachen_sa0037_device::write_h) +{ + LOG_MMC(("sa0037 write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + prg32(data >> 3); + chr8(data, CHRROM); +} + +/*------------------------------------------------- + + Sachen SA72007 bootleg boards + + Games: Sidewinder + + iNES: mapper 145 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sachen_sa72007_device::write_l) +{ + LOG_MMC(("SA72007 write_l, offset: %04x, data: %02x\n", offset, data)); + + /* only if we are at 0x4100 + k * 0x200, but 0x4100 is offset = 0 */ + if (!(offset & 0x100)) + chr8(data >> 7, CHRROM); +} + +/*------------------------------------------------- + + Sachen SA72008 bootleg boards + + Games: Jovial Race, Qi Wang + + iNES: mapper 133 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sachen_sa72008_device::write_l) +{ + LOG_MMC(("SA72008 write_l, offset: %04x, data: %02x\n", offset, data)); + + prg32(data >> 2); + chr8(data, CHRROM); +} + +/*------------------------------------------------- + + Sachen TCA-01 bootleg boards + + iNES: mapper 143 + + Games: Dancing Blocks, Magic Mathematic + + In MESS: Supported. + + -------------------------------------------------*/ + +READ8_MEMBER(nes_sachen_tca01_device::read_l) +{ + LOG_MMC(("TCA-01 read_l, offset: %04x\n", offset)); + + /* the address is read only if we are at 0x4100 + k * 0x200, but 0x4100 is offset = 0 */ + if (!(offset & 0x100)) + return (~offset & 0x3f) | 0x40; + else + return 0x00; +} + +/*------------------------------------------------- + + Sachen TCU-01 bootleg boards + + Games: Challenge of the Dragon, Chinese Kungfu + + iNES: mapper 147 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sachen_tcu01_device::write_l) +{ + LOG_MMC(("TCU-01 write_l, offset: %04x, data: %02x\n", offset, data)); + + if ((offset & 0x103) == 0x002) + { + prg32(((data >> 6) & 0x02) | ((data >> 2) & 0x01)); + chr8(data >> 3, CHRROM); + } +} + +/*------------------------------------------------- + + Sachen TCU-02 bootleg boards + + Games: Wei Lai Xiao Zi + + iNES: mapper 136 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sachen_tcu02_device::write_l) +{ + LOG_MMC(("TCU-02 write_l, offset: %04x, data: %02x\n", offset, data)); + + if ((offset & 0x103) == 0x002) + { + m_latch = (data & 0x30) | ((data + 3) & 0x0f); + chr8(m_latch, CHRROM); + } +} + +READ8_MEMBER(nes_sachen_tcu02_device::read_l) +{ + LOG_MMC(("TCU-02 read_l, offset: %04x\n", offset)); + + if ((offset & 0x103) == 0x000) + return m_latch | 0x40; + else + return 0x00; +} + +/*------------------------------------------------- + + Sachen 74x374 bootleg boards + + Games: Chess Academy, Chinese Checkers Jpn, Mahjong Academy, + Olympic IQ, Poker II, Tasac [150], Poker III [243] + + iNES: mappers 150 & 243 + + -------------------------------------------------*/ + +void nes_sachen_74x374_device::set_mirror(UINT8 nt) // also used by mappers 137, 138, 139, 141 +{ + switch (nt) + { + case 0: + case 1: + set_nt_mirroring(nt ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 2: + set_nt_page(0, CIRAM, 0, 1); + set_nt_page(1, CIRAM, 1, 1); + set_nt_page(2, CIRAM, 1, 1); + set_nt_page(3, CIRAM, 1, 1); + break; + case 3: + set_nt_mirroring(PPU_MIRROR_LOW); + break; + default: + LOG_MMC(("Mapper set NT to invalid value %02x", nt)); + break; + } +} + + +WRITE8_MEMBER(nes_sachen_74x374_device::write_l) +{ + LOG_MMC(("Sachen 74*374 write_l, offset: %04x, data: %02x\n", offset, data)); + + /* write happens only if we are at 0x4100 + k * 0x200, but 0x4100 is offset = 0 */ + if (!(offset & 0x100)) + { + if (!(offset & 0x01)) + m_latch = data & 0x07; + else + { + switch (m_latch) + { + case 0x02: + m_mmc_vrom_bank = (m_mmc_vrom_bank & ~0x08) | ((data << 3) & 0x08); + chr8(m_mmc_vrom_bank, CHRROM); + prg32(data & 0x01); + break; + case 0x04: + m_mmc_vrom_bank = (m_mmc_vrom_bank & ~0x04) | ((data << 2) & 0x04); + chr8(m_mmc_vrom_bank, CHRROM); + break; + case 0x05: + prg32(data & 0x07); + break; + case 0x06: + m_mmc_vrom_bank = (m_mmc_vrom_bank & ~0x03) | ((data << 0) & 0x03); + chr8(m_mmc_vrom_bank, CHRROM); + break; + case 0x07: + set_mirror((data >> 1) & 0x03); + break; + default: + break; + } + } + } +} + +READ8_MEMBER(nes_sachen_74x374_device::read_l) +{ + LOG_MMC(("Sachen 74*374 read_l, offset: %04x", offset)); + + /* read happens only if we are at 0x4100 + k * 0x200, but 0x4100 is offset = 0 */ + if (!(offset & 0x100)) + return (~m_latch & 0x3f) /* ^ dips*/; // we would need to check the Dips here + else + return 0; +} + +WRITE8_MEMBER(nes_sachen_74x374_alt_device::write_l) +{ + LOG_MMC(("Sachen 74*374 Alt write_l, offset: %04x, data: %02x\n", offset, data)); + + /* write happens only if we are at 0x4100 + k * 0x200, but 0x4100 is offset = 0 */ + if (!(offset & 0x100)) + { + if (!(offset & 0x01)) + m_latch = data; + else + { + switch (m_latch & 0x07) + { + case 0x00: + prg32(0); + chr8(3, CHRROM); + break; + case 0x02: + m_mmc_vrom_bank = (m_mmc_vrom_bank & ~0x08) | ((data << 3) & 0x08); + chr8(m_mmc_vrom_bank, CHRROM); + break; + case 0x04: + m_mmc_vrom_bank = (m_mmc_vrom_bank & ~0x01) | ((data << 0) & 0x01); + chr8(m_mmc_vrom_bank, CHRROM); + break; + case 0x05: + prg32(data & 0x01); + break; + case 0x06: + m_mmc_vrom_bank = (m_mmc_vrom_bank & ~0x06) | ((data << 1) & 0x06); + chr8(m_mmc_vrom_bank, CHRROM); + break; + case 0x07: + set_mirror(BIT(data, 0)); + break; + default: + break; + } + } + } +} + +/*------------------------------------------------- + + Sachen S8259 bootleg boards + + iNES: mapper 141 (A), 138 (B), 139 (C), 137 (D) + + -------------------------------------------------*/ + +void nes_sachen_8259a_device::chr_update() +{ + UINT8 bank_helper1 = m_reg[7] & 0x01; + UINT8 bank_helper2 = (m_reg[4] & 0x07) << 3; + + if (m_chr_source == CHRROM) + { + chr2_0(((m_reg[bank_helper1 ? 0 : 0] & 0x07) | bank_helper2) << 1 | 0, CHRROM); + chr2_2(((m_reg[bank_helper1 ? 0 : 1] & 0x07) | bank_helper2) << 1 | 1, CHRROM); + chr2_4(((m_reg[bank_helper1 ? 0 : 2] & 0x07) | bank_helper2) << 1 | 0, CHRROM); + chr2_6(((m_reg[bank_helper1 ? 0 : 3] & 0x07) | bank_helper2) << 1 | 1, CHRROM); + } +} + +WRITE8_MEMBER(nes_sachen_8259a_device::write_l) +{ + LOG_MMC(("Sachen 8259 write_l, offset: %04x, data: %02x\n", offset, data)); + + /* write happens only if we are at 0x4100 + k * 0x200, but 0x4100 is offset = 0 */ + if (!(offset & 0x100)) + { + if (!(offset & 0x01)) + m_latch = data & 0x07; + else + { + m_reg[m_latch] = data; + + switch (m_latch) + { + case 0x05: + prg32(data); + break; + case 0x07: + set_mirror(BIT(data, 0) ? 0 : (data >> 1) & 0x03); + break; + default: + chr_update(); + break; + } + } + } +} + +void nes_sachen_8259b_device::chr_update() +{ + UINT8 bank_helper1 = m_reg[7] & 0x01; + UINT8 bank_helper2 = (m_reg[4] & 0x07) << 3; + + if (m_chr_source == CHRROM) + { + chr2_0(((m_reg[bank_helper1 ? 0 : 0] & 0x07) | bank_helper2), CHRROM); + chr2_2(((m_reg[bank_helper1 ? 0 : 1] & 0x07) | bank_helper2), CHRROM); + chr2_4(((m_reg[bank_helper1 ? 0 : 2] & 0x07) | bank_helper2), CHRROM); + chr2_6(((m_reg[bank_helper1 ? 0 : 3] & 0x07) | bank_helper2), CHRROM); + } +} + +void nes_sachen_8259c_device::chr_update() +{ + UINT8 bank_helper1 = m_reg[7] & 0x01; + UINT8 bank_helper2 = (m_reg[4] & 0x07) << 3; + + if (m_chr_source == CHRROM) + { + chr2_0(((m_reg[bank_helper1 ? 0 : 0] & 0x07) | bank_helper2) << 2 | 0, CHRROM); + chr2_2(((m_reg[bank_helper1 ? 0 : 1] & 0x07) | bank_helper2) << 2 | 1, CHRROM); + chr2_4(((m_reg[bank_helper1 ? 0 : 2] & 0x07) | bank_helper2) << 2 | 2, CHRROM); + chr2_6(((m_reg[bank_helper1 ? 0 : 3] & 0x07) | bank_helper2) << 2 | 3, CHRROM); + } +} + +void nes_sachen_8259d_device::chr_update() +{ + if (m_chr_source == CHRROM) + { + chr1_0((m_reg[0] & 0x07), CHRROM); + chr1_1((m_reg[1] & 0x07) | (m_reg[4] << 4 & 0x10), CHRROM); + chr1_2((m_reg[2] & 0x07) | (m_reg[4] << 3 & 0x10), CHRROM); + chr1_3((m_reg[3] & 0x07) | (m_reg[4] << 2 & 0x10) | (m_reg[6] << 3 & 0x08), CHRROM); + } +} diff --git a/src/devices/bus/nes/sachen.h b/src/devices/bus/nes/sachen.h new file mode 100644 index 00000000000..d6604578d3a --- /dev/null +++ b/src/devices/bus/nes/sachen.h @@ -0,0 +1,261 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_SACHEN_H +#define __NES_SACHEN_H + +#include "nxrom.h" + + +// ======================> nes_sachen_sa009_device + +class nes_sachen_sa009_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sachen_sa009_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); +}; + + +// ======================> nes_sachen_sa0036_device + +class nes_sachen_sa0036_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sachen_sa0036_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_sachen_sa0037_device + +class nes_sachen_sa0037_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sachen_sa0037_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_sachen_sa72007_device + +class nes_sachen_sa72007_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sachen_sa72007_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); +}; + + +// ======================> nes_sachen_sa72008_device + +class nes_sachen_sa72008_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sachen_sa72008_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); +}; + + +// ======================> nes_sachen_tca01_device + +class nes_sachen_tca01_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sachen_tca01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + + virtual void pcb_reset(); +}; + + +// ======================> nes_sachen_tcu01_device + +class nes_sachen_tcu01_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sachen_tcu01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_m) { write_l(space, (offset + 0x100) & 0xfff, data, mem_mask); } + virtual DECLARE_WRITE8_MEMBER(write_h) { write_l(space, (offset + 0x100) & 0xfff, data, mem_mask); } + + virtual void pcb_reset(); +}; + + +// ======================> nes_sachen_tcu02_device + +class nes_sachen_tcu02_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sachen_tcu02_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; +}; + + +// ======================> nes_sachen_74x374_device + +class nes_sachen_74x374_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sachen_74x374_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_sachen_74x374_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); + +protected: + void set_mirror(UINT8 nt); + UINT8 m_latch, m_mmc_vrom_bank; +}; + + +// ======================> nes_sachen_74x374_alt_device + +class nes_sachen_74x374_alt_device : public nes_sachen_74x374_device +{ +public: + // construction/destruction + nes_sachen_74x374_alt_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_READ8_MEMBER(read_l) { return 0xff; } // no read_l here + virtual DECLARE_WRITE8_MEMBER(write_l); +}; + + +// ======================> nes_sachen_8259a_device + +class nes_sachen_8259a_device : public nes_sachen_74x374_device +{ +public: + // construction/destruction + nes_sachen_8259a_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_sachen_8259a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_m) { write_l(space, (offset + 0x100) & 0xfff, data, mem_mask); } + + virtual void pcb_reset(); + +protected: + virtual void chr_update(); + UINT8 m_reg[8]; +}; + + +// ======================> nes_sachen_8259b_device + +class nes_sachen_8259b_device : public nes_sachen_8259a_device +{ +public: + // construction/destruction + nes_sachen_8259b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + +protected: + virtual void chr_update(); +}; + + +// ======================> nes_sachen_8259c_device + +class nes_sachen_8259c_device : public nes_sachen_8259a_device +{ +public: + // construction/destruction + nes_sachen_8259c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + +protected: + virtual void chr_update(); +}; + + +// ======================> nes_sachen_8259d_device + +class nes_sachen_8259d_device : public nes_sachen_8259a_device +{ +public: + // construction/destruction + nes_sachen_8259d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual void pcb_reset(); + +protected: + virtual void chr_update(); +}; + + + + + +// device type definition +extern const device_type NES_SACHEN_SA009; +extern const device_type NES_SACHEN_SA0036; +extern const device_type NES_SACHEN_SA0037; +extern const device_type NES_SACHEN_SA72007; +extern const device_type NES_SACHEN_SA72008; +extern const device_type NES_SACHEN_TCA01; +extern const device_type NES_SACHEN_TCU01; +extern const device_type NES_SACHEN_TCU02; +extern const device_type NES_SACHEN_74X374; +extern const device_type NES_SACHEN_74X374_ALT; +extern const device_type NES_SACHEN_8259A; +extern const device_type NES_SACHEN_8259B; +extern const device_type NES_SACHEN_8259C; +extern const device_type NES_SACHEN_8259D; + +#endif diff --git a/src/devices/bus/nes/somari.c b/src/devices/bus/nes/somari.c new file mode 100644 index 00000000000..634aad58b68 --- /dev/null +++ b/src/devices/bus/nes/somari.c @@ -0,0 +1,421 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Somari Team PCBs + + + Here we emulate the Somari Team PCBs [mapper 116] + + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "somari.h" + +#include "cpu/m6502/m6502.h" + + +#ifdef NES_PCB_DEBUG +#define VERBOSE 1 +#else +#define VERBOSE 0 +#endif + +#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0) + +#define SOMARI_VRC2_MODE 0 +#define SOMARI_MMC3_MODE 1 +#define SOMARI_MMC1_MODE 2 +#define SOMARI_MMC1_MODE_AGAIN 3 + + +//------------------------------------------------- +// constructor +//------------------------------------------------- + +const device_type NES_SOMARI = &device_creator; + + +nes_somari_device::nes_somari_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_SOMARI, "NES Cart Team Somari PCB", tag, owner, clock, "nes_somari", __FILE__) +{ +} + + + +void nes_somari_device::device_start() +{ + common_start(); + save_item(NAME(m_board_mode)); + + // MMC3 + save_item(NAME(m_mmc_prg_bank)); + save_item(NAME(m_mmc_vrom_bank)); + save_item(NAME(m_latch)); + save_item(NAME(m_prg_base)); + save_item(NAME(m_prg_mask)); + save_item(NAME(m_chr_base)); + save_item(NAME(m_chr_mask)); + save_item(NAME(m_mmc3_mirror_reg)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); + save_item(NAME(m_irq_clear)); + + // MMC1 + save_item(NAME(m_count)); + save_item(NAME(m_mmc1_latch)); + save_item(NAME(m_mmc1_reg)); + + // VRC2 + save_item(NAME(m_vrc_prg_bank)); + save_item(NAME(m_vrc_vrom_bank)); + save_item(NAME(m_vrc_mirror_reg)); +} + +void nes_somari_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_board_mode = 2; // mode + + // MMC3 + m_prg_base = m_chr_base = 0; + m_prg_mask = 0xff; + m_chr_mask = 0xff; + m_latch = 0; + m_mmc_prg_bank[0] = 0x3c; + m_mmc_prg_bank[1] = 0x3d; + m_mmc_prg_bank[2] = 0x3e; + m_mmc_prg_bank[3] = 0x3f; + m_mmc_vrom_bank[0] = 0x00; + m_mmc_vrom_bank[1] = 0x01; + m_mmc_vrom_bank[2] = 0x04; + m_mmc_vrom_bank[3] = 0x05; + m_mmc_vrom_bank[4] = 0x06; + m_mmc_vrom_bank[5] = 0x07; + m_mmc3_mirror_reg = 0; + + m_alt_irq = 0; + m_irq_enable = 0; + m_irq_count = m_irq_count_latch = 0; + m_irq_clear = 0; + + // MMC1 regs + m_count = 0; + m_mmc1_latch = 0; + m_mmc1_reg[0] = 0x0c; + m_mmc1_reg[1] = 0x00; + m_mmc1_reg[2] = 0x00; + m_mmc1_reg[3] = 0x00; + + // VRC2 regs + m_vrc_prg_bank[0] = 0x00; + m_vrc_prg_bank[1] = 0x01; + for (int i = 0; i < 8; ++i) + m_vrc_vrom_bank[i] = i; + bank_update_switchmode(); + m_vrc_mirror_reg = 0; +} + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + SOMERI TEAM + + iNES: mapper 116 + + + Emulation note about regs in MESS: currently, + - m_mmc_prg_bank[n] for n=0,...,3 represent the MMC3 PRG banks (inherited from base class) + - m_mmc_vrom_bank[n] for n=0,...,5 represent the MMC3 CHR banks (inherited from base class) + + - m_mmc1_reg[n] for n=0,1,2,3 represent the MMC1 regs + - m_count and m_mmc1_latch are additional variables for MMC1 (notice that MMC3 uses a diff m_latch!) + + - m_vrc_prg_bank[n] for n=0,1 represent the VRC2 PRG banks + - m_vrc_vrom_bank[n] for n=0,...,7 represent the VRC2 CHR banks + + + In MESS: Preliminary support + + -------------------------------------------------*/ + +// MMC1 Mode emulation +WRITE8_MEMBER(nes_somari_device::mmc1_w) +{ + assert(m_board_mode == 2); + + if (data & 0x80) + { + m_count = 0; + m_mmc1_latch = 0; + + m_mmc1_reg[0] |= 0x0c; + update_prg(); + return; + } + + if (m_count < 5) + { + if (m_count == 0) m_mmc1_latch = 0; + m_mmc1_latch >>= 1; + m_mmc1_latch |= (data & 0x01) ? 0x10 : 0x00; + m_count++; + } + + if (m_count == 5) + { + m_mmc1_reg[(offset & 0x6000) >> 13] = m_mmc1_latch; + update_mirror(); + update_prg(); + update_chr(); + + m_count = 0; + } +} + +// MMC3 Mode emulation +WRITE8_MEMBER(nes_somari_device::mmc3_w) +{ + UINT8 mmc_helper, cmd; + + assert(m_board_mode == 1); + + switch (offset & 0x6001) + { + case 0x0000: + mmc_helper = m_latch ^ data; + m_latch = data; + + if (mmc_helper & 0x40) + update_prg(); + + if (mmc_helper & 0x80) + update_chr(); + break; + + case 0x0001: + cmd = m_latch & 0x07; + switch (cmd) + { + case 0: case 1: + case 2: case 3: case 4: case 5: + m_mmc_vrom_bank[cmd] = data; + update_chr(); + break; + case 6: + case 7: + m_mmc_prg_bank[cmd - 6] = data & 0x3f; + update_prg(); + break; + } + break; + + case 0x2000: + m_mmc3_mirror_reg = data & 1; + update_mirror(); + break; + case 0x2001: break; + case 0x4000: m_irq_count_latch = data; break; + case 0x4001: m_irq_count = 0; break; + case 0x6000: m_irq_enable = 0; m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); break; + case 0x6001: m_irq_enable = 1; break; + } +} + +// VRC2 Mode emulation +WRITE8_MEMBER(nes_somari_device::vrc2_w) +{ + UINT8 bank, shift; + + assert(m_board_mode == 0); + + switch (offset & 0x7000) + { + case 0x0000: + m_vrc_prg_bank[0] = data & 0x1f; + update_prg(); + break; + + case 0x1000: + m_vrc_mirror_reg = data & 1; + update_mirror(); + break; + + case 0x2000: + m_vrc_prg_bank[1] = data & 0x1f; + update_prg(); + break; + + case 0x3000: + case 0x4000: + case 0x5000: + case 0x6000: + // this makes no sense for vrc2 and breaks somari, but it's ok for garousp!! + bank = ((offset & 0x7000) - 0x3000) / 0x0800 + BIT(offset, 1); + shift = BIT(offset, 2) * 4; + data = (data & 0x0f) << shift; + m_vrc_vrom_bank[bank] = data; + + update_chr(); + break; + } +} + + +void nes_somari_device::update_prg() +{ + switch (m_board_mode) + { + case SOMARI_VRC2_MODE: + prg8_89(m_vrc_prg_bank[0]); + prg8_ab(m_vrc_prg_bank[1]); + prg8_cd(0x3e); + prg8_ef(0x3f); + break; + case SOMARI_MMC3_MODE: + { + UINT8 prg_flip = (m_latch & 0x40) ? 2 : 0; + prg8_x(0, m_mmc_prg_bank[0 ^ prg_flip]); + prg8_x(1, m_mmc_prg_bank[1]); + prg8_x(2, m_mmc_prg_bank[2 ^ prg_flip]); + prg8_x(3, m_mmc_prg_bank[3]); + } + break; + case SOMARI_MMC1_MODE: +// case SOMARI_MMC1_MODE_AGAIN: + { + UINT8 prg_offset = m_mmc1_reg[1] & 0x10; + + switch (m_mmc1_reg[0] & 0x0c) + { + case 0x00: + case 0x04: + prg32((prg_offset + m_mmc1_reg[3]) >> 1); + break; + case 0x08: + prg16_89ab(prg_offset + 0); + prg16_cdef(prg_offset + m_mmc1_reg[3]); + break; + case 0x0c: + prg16_89ab(prg_offset + m_mmc1_reg[3]); + prg16_cdef(prg_offset + 0x0f); + break; + } + } + break; + } +} + +void nes_somari_device::update_chr() +{ + switch (m_board_mode) + { + case SOMARI_VRC2_MODE: + for (int i = 0; i < 8; i++) + chr1_x(i, m_chr_base | m_vrc_vrom_bank[i], CHRROM); + break; + case SOMARI_MMC3_MODE: + { + UINT8 chr_page = (m_latch & 0x80) >> 5; + chr1_x(chr_page ^ 0, m_chr_base | ((m_mmc_vrom_bank[0] & ~0x01)), CHRROM); + chr1_x(chr_page ^ 1, m_chr_base | ((m_mmc_vrom_bank[0] | 0x01)), CHRROM); + chr1_x(chr_page ^ 2, m_chr_base | ((m_mmc_vrom_bank[1] & ~0x01)), CHRROM); + chr1_x(chr_page ^ 3, m_chr_base | ((m_mmc_vrom_bank[1] | 0x01)), CHRROM); + chr1_x(chr_page ^ 4, m_chr_base | (m_mmc_vrom_bank[2]), CHRROM); + chr1_x(chr_page ^ 5, m_chr_base | (m_mmc_vrom_bank[3]), CHRROM); + chr1_x(chr_page ^ 6, m_chr_base | (m_mmc_vrom_bank[4]), CHRROM); + chr1_x(chr_page ^ 7, m_chr_base | (m_mmc_vrom_bank[5]), CHRROM); + } + break; + case SOMARI_MMC1_MODE: +// case SOMARI_MMC1_MODE_AGAIN: + if (BIT(m_mmc1_reg[0], 4)) + { + chr4_0(m_mmc1_reg[1] & 0x1f, CHRROM); + chr4_4(m_mmc1_reg[2] & 0x1f, CHRROM); + } + else + chr8((m_mmc1_reg[1] & 0x1f) >> 1, CHRROM); + break; + } +} + +void nes_somari_device::update_mirror() +{ + switch (m_board_mode) + { + case SOMARI_VRC2_MODE: + set_nt_mirroring(m_vrc_mirror_reg ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case SOMARI_MMC3_MODE: + set_nt_mirroring(m_mmc3_mirror_reg ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case SOMARI_MMC1_MODE: +// case SOMARI_MMC1_MODE_AGAIN: + switch (m_mmc1_reg[0] & 0x03) + { + case 0x00: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 0x01: set_nt_mirroring(PPU_MIRROR_HIGH); break; + case 0x02: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 0x03: set_nt_mirroring(PPU_MIRROR_HORZ); break; + } + break; + } +} + + +WRITE8_MEMBER(nes_somari_device::write_h) +{ + LOG_MMC(("somari write_h, mode %d, offset: %04x, data: %02x\n", m_board_mode, offset, data)); + + switch (m_board_mode) + { + case SOMARI_VRC2_MODE: vrc2_w(space, offset, data, mem_mask); break; + case SOMARI_MMC3_MODE: mmc3_w(space, offset, data, mem_mask); break; + case SOMARI_MMC1_MODE: mmc1_w(space, offset, data, mem_mask); break; + } +} + +void nes_somari_device::bank_update_switchmode() +{ + switch (m_board_mode) + { + case SOMARI_VRC2_MODE: + break; + case SOMARI_MMC3_MODE: + break; + case SOMARI_MMC1_MODE: + break; + } + update_mirror(); + update_prg(); + update_chr(); +} + +WRITE8_MEMBER(nes_somari_device::write_m) +{ + LOG_MMC(("somari write_m, offset: %04x, data: %02x\n", offset, data)); + + if (offset & 0x100) + { + m_board_mode = data & 0x03; + m_chr_base = ((m_board_mode & 0x04) << 6); + if (m_board_mode != 1) + m_irq_enable = 0; + bank_update_switchmode(); + } +} diff --git a/src/devices/bus/nes/somari.h b/src/devices/bus/nes/somari.h new file mode 100644 index 00000000000..726df0a4926 --- /dev/null +++ b/src/devices/bus/nes/somari.h @@ -0,0 +1,54 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_SOMARI_H +#define __NES_SOMARI_H + +#include "mmc3.h" + + +// ======================> nes_somari_device + +class nes_somari_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_somari_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l) { write_m(space, offset + 0x100, data, mem_mask); } + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(mmc1_w); + virtual DECLARE_WRITE8_MEMBER(mmc3_w); + virtual DECLARE_WRITE8_MEMBER(vrc2_w); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +private: + void update_prg(); + void update_chr(); + void update_mirror(); + void bank_update_switchmode(); + + UINT8 m_board_mode; + + // MMC3 - inherited from txrom + UINT8 m_mmc3_mirror_reg; + + // MMC1 + UINT8 m_count; + UINT8 m_mmc1_latch; + UINT8 m_mmc1_reg[4]; + + // VRC2 + UINT8 m_vrc_prg_bank[2]; + UINT8 m_vrc_vrom_bank[8]; + UINT8 m_vrc_mirror_reg; +}; + + + +// device type definition +extern const device_type NES_SOMARI; + +#endif diff --git a/src/devices/bus/nes/sunsoft.c b/src/devices/bus/nes/sunsoft.c new file mode 100644 index 00000000000..48d95c05727 --- /dev/null +++ b/src/devices/bus/nes/sunsoft.c @@ -0,0 +1,628 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Sunsoft PCBs + + + Here we emulate the following PCBs + + * Sunsoft-1 [mapper 184] + * Sunsoft-2 [mapper 89 & 93] + * Sunsoft-3 [mapper 67] + * Sunsoft-4 [mapper 68] + * Sunsoft FME7 & Sunsoft-5A [mapper 69] + * Sunsoft-5B [mapper 69] + + TODO: + - check 1-line glitches due to IRQ in Sunsoft-3 + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "sunsoft.h" + +#include "cpu/m6502/m6502.h" +#include "sound/ay8910.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_SUNSOFT_1 = &device_creator; +const device_type NES_SUNSOFT_2 = &device_creator; +const device_type NES_SUNSOFT_3 = &device_creator; +const device_type NES_SUNSOFT_4 = &device_creator; +const device_type NES_SUNSOFT_FME7 = &device_creator; +const device_type NES_SUNSOFT_5 = &device_creator; + + +nes_sunsoft_1_device::nes_sunsoft_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SUNSOFT_1, "NES Cart Sunsoft 1 PCB", tag, owner, clock, "nes_sun1", __FILE__) +{ +} + +nes_sunsoft_2_device::nes_sunsoft_2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SUNSOFT_2, "NES Cart Sunsoft 2 PCB", tag, owner, clock, "nes_sun2", __FILE__) +{ +} + +nes_sunsoft_3_device::nes_sunsoft_3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SUNSOFT_3, "NES Cart Sunsoft 3 PCB", tag, owner, clock, "nes_sun3", __FILE__) +{ +} + +nes_sunsoft_4_device::nes_sunsoft_4_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_sunsoft_4_device::nes_sunsoft_4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SUNSOFT_4, "NES Cart Sunsoft 4 PCB", tag, owner, clock, "nes_sun4", __FILE__) +{ +} + +nes_sunsoft_fme7_device::nes_sunsoft_fme7_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_sunsoft_fme7_device::nes_sunsoft_fme7_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SUNSOFT_4, "NES Cart Sunsoft FME7 PCB", tag, owner, clock, "nes_fme7", __FILE__) +{ +} + +nes_sunsoft_5_device::nes_sunsoft_5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sunsoft_fme7_device(mconfig, NES_SUNSOFT_5, "NES Cart Sunsoft 5A/5B PCB", tag, owner, clock, "nes_sun5", __FILE__), + m_ym2149(*this, "ay") +{ +} + + +void nes_sunsoft_1_device::device_start() +{ + common_start(); +} + +void nes_sunsoft_1_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); +} + +void nes_sunsoft_2_device::device_start() +{ + common_start(); +} + +void nes_sunsoft_2_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); + if (m_pcb_ctrl_mirror) + set_nt_mirroring(PPU_MIRROR_LOW); +} + +void nes_sunsoft_3_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_toggle)); + save_item(NAME(m_irq_count)); +} + +void nes_sunsoft_3_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_toggle = 0; + m_irq_count = 0; + m_irq_enable = 0; +} + +void nes_sunsoft_4_device::device_start() +{ + common_start(); + save_item(NAME(m_latch1)); + save_item(NAME(m_latch2)); + save_item(NAME(m_reg)); + save_item(NAME(m_wram_enable)); +} + +void nes_sunsoft_4_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_reg = 0; + m_latch1 = 0; + m_latch2 = 0; + m_wram_enable = 0; +} + +void nes_sunsoft_fme7_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + // this has to be hardcoded because some some scanline code only suits NTSC... it will be fixed with PPU rewrite + irq_timer->adjust(attotime::zero, 0, attotime::from_hz((21477272.724 / 12))); +// irq_timer->adjust(attotime::zero, 0, machine().device("maincpu")->cycles_to_attotime(1)); + + save_item(NAME(m_wram_bank)); + save_item(NAME(m_latch)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); +} + +void nes_sunsoft_fme7_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_wram_bank = 0; + + m_latch = 0; + m_irq_enable = 0; + m_irq_count = 0; +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Sunsoft-1 board emulation + + Games: Atlantis no Nazo, Kanshakudama Nage Kantarou no + Toukaidou Gojuusan Tsugi, Wing of Madoola, Fantasy Zone + + iNES: mapper 184 (Fantasy Zone uses this board with no + CHRROM, and the register switches PRG banks) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sunsoft_1_device::write_m) +{ + LOG_MMC(("Sunsoft 1 write_m, offset: %04x, data: %02x\n", offset, data)); + + if (m_vrom_chunks) + { + chr4_0(data & 0x0f, CHRROM); + chr4_4(data >> 4, CHRROM); + } + else + prg16_89ab(data & 0x0f); +} + +/*------------------------------------------------- + + Sunsoft-2 board emulation + + The two games using this board have incompatible mirroring + wiring, making necessary two distinct mappers + + iNES: mapper 89 & 93 + + -------------------------------------------------*/ + +// there are two 'variants' depending on hardwired or mapper ctrl mirroring + +WRITE8_MEMBER(nes_sunsoft_2_device::write_h) +{ + UINT8 helper = (data & 0x07) | ((data & 0x80) ? 0x08 : 0x00); + LOG_MMC(("Sunsoft 2 write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + if (m_pcb_ctrl_mirror) + set_nt_mirroring(BIT(data, 3) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW); + + if (m_vrom_chunks) + chr8(helper, CHRROM); + + prg16_89ab(data >> 4); +} + +/*------------------------------------------------- + + Sunsoft-3 board emulation + + The two games using this board have incompatible mirroring + wiring, making necessary two distinct mappers & pcb_id + + iNES: mapper 67 + + -------------------------------------------------*/ + + +void nes_sunsoft_3_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if (m_irq_enable) + { + if (!m_irq_count) + { + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + m_irq_count = 0xffff; + m_irq_enable = 0; + } + else + m_irq_count--; + } + } +} + +WRITE8_MEMBER(nes_sunsoft_3_device::write_h) +{ + LOG_MMC(("Sunsoft 3 write_h, offset %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7800) + { + case 0x0800: + chr2_0(data, CHRROM); + break; + case 0x1800: + chr2_2(data, CHRROM); + break; + case 0x2800: + chr2_4(data, CHRROM); + break; + case 0x3800: + chr2_6(data, CHRROM); + break; + case 0x4000: + case 0x4800: + m_irq_toggle ^= 1; + if (m_irq_toggle) + m_irq_count = (m_irq_count & 0x00ff) | (data << 8); + else + m_irq_count = (m_irq_count & 0xff00) | data; + break; + case 0x5800: + m_irq_enable = BIT(data, 4); + m_irq_toggle = 0; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x6800: + switch (data & 3) + { + 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 0x7800: + prg16_89ab(data); + break; + default: + LOG_MMC(("Sunsoft 3 write_h uncaught write, offset: %04x, data: %02x\n", offset, data)); + break; + } +} + +/*------------------------------------------------- + + Sunsoft-4 board emulation + + Games: AfterBurner & Maharaja + + iNES: mapper 68 + + -------------------------------------------------*/ + +void nes_sunsoft_4_device::sun4_mirror( int mirror, int mirr0, int mirr1 ) +{ + switch (mirror) + { + 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; + case 0x10: + set_nt_page(0, VROM, mirr0 | 0x80, 0); + set_nt_page(1, VROM, mirr1 | 0x80, 0); + set_nt_page(2, VROM, mirr0 | 0x80, 0); + set_nt_page(3, VROM, mirr1 | 0x80, 0); + break; + case 0x11: + set_nt_page(0, VROM, mirr0 | 0x80, 0); + set_nt_page(1, VROM, mirr0 | 0x80, 0); + set_nt_page(2, VROM, mirr1 | 0x80, 0); + set_nt_page(3, VROM, mirr1 | 0x80, 0); + break; + case 0x12: + set_nt_page(0, VROM, mirr0 | 0x80, 0); + set_nt_page(1, VROM, mirr0 | 0x80, 0); + set_nt_page(2, VROM, mirr0 | 0x80, 0); + set_nt_page(3, VROM, mirr0 | 0x80, 0); + break; + case 0x13: + set_nt_page(0, VROM, mirr1 | 0x80, 0); + set_nt_page(1, VROM, mirr1 | 0x80, 0); + set_nt_page(2, VROM, mirr1 | 0x80, 0); + set_nt_page(3, VROM, mirr1 | 0x80, 0); + break; + } +} + +WRITE8_MEMBER(nes_sunsoft_4_device::sun4_write) +{ + LOG_MMC(("Sunsoft 4 write_h, offset %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x0000: + chr2_0(data, CHRROM); + break; + case 0x1000: + chr2_2(data, CHRROM); + break; + case 0x2000: + chr2_4(data, CHRROM); + break; + case 0x3000: + chr2_6(data, CHRROM); + break; + case 0x4000: + m_latch1 = data & 0x7f; + sun4_mirror(m_reg, m_latch1, m_latch2); + break; + case 0x5000: + m_latch2 = data & 0x7f; + sun4_mirror(m_reg, m_latch1, m_latch2); + break; + case 0x6000: + m_reg = data & 0x13; + sun4_mirror(m_reg, m_latch1, m_latch2); + break; + case 0x7000: + prg16_89ab(data & 0x0f); + m_wram_enable = BIT(data, 4); + break; + default: + LOG_MMC(("Sunsoft 4 write_h uncaught write, offset: %04x, data: %02x\n", offset, data)); + break; + } +} + +WRITE8_MEMBER(nes_sunsoft_4_device::write_m) +{ + LOG_MMC(("Sunsoft 4 write_m, offset: %04x, data: %02x\n", offset, data)); + + if (!m_battery.empty() && m_wram_enable) + m_battery[offset & (m_battery.size() - 1)] = data; + if (!m_prgram.empty() && m_wram_enable) + m_prgram[offset & (m_prgram.size() - 1)] = data; +} + +READ8_MEMBER(nes_sunsoft_4_device::read_m) +{ + LOG_MMC(("Sunsoft 4 read_m, offset: %04x\n", offset)); + + if (!m_battery.empty() && m_wram_enable) + return m_battery[offset & (m_battery.size() - 1)]; + if (!m_prgram.empty() && m_wram_enable) + return m_prgram[offset & (m_prgram.size() - 1)]; + + return m_open_bus; // open bus +} + +/*------------------------------------------------- + + JxROM & Sunsoft 5A / 5B / FME7 board emulation + + Notice that Sunsoft-5B = FME7 + sound chip (the latter being + currently unemulated in MESS) + + iNES: mapper 69 + + -------------------------------------------------*/ + +void nes_sunsoft_fme7_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + if ((m_irq_enable & 0x80)) // bit7, counter decrement + { + if (!m_irq_count) + { + m_irq_count = 0xffff; + if (m_irq_enable & 0x01) // bit0, trigger enable + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + } + else + m_irq_count--; + } + } +} + +WRITE8_MEMBER(nes_sunsoft_fme7_device::fme7_write) +{ + LOG_MMC(("fme7_write, offset %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6000) + { + case 0x0000: + m_latch = data & 0x0f; + break; + + case 0x2000: + switch (m_latch) + { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + chr1_x(m_latch, data, CHRROM); + break; + + case 8: + m_wram_bank = data; + break; + case 9: + prg8_89(data); + break; + case 0x0a: + prg8_ab(data); + break; + case 0x0b: + prg8_cd(data); + break; + case 0x0c: + 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 0x0d: + m_irq_enable = data; + if (!(m_irq_enable & 1)) + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x0e: + m_irq_count = (m_irq_count & 0xff00) | data; + break; + case 0x0f: + m_irq_count = (m_irq_count & 0x00ff) | (data << 8); + break; + } + break; + + default: + logerror("Sunsoft FME7 write_h uncaught %04x value: %02x\n", offset + 0x8000, data); + break; + } +} + +WRITE8_MEMBER(nes_sunsoft_fme7_device::write_m) +{ + UINT8 bank = m_wram_bank & 0x3f; + LOG_MMC(("Sunsoft FME7 write_m, offset: %04x, data: %02x\n", offset, data)); + + if (!(m_wram_bank & 0x40)) // is PRG ROM, no write + return; + else if (m_wram_bank & 0x80) // is PRG RAM + { + if (!m_battery.empty()) + m_battery[((bank * 0x2000) + offset) & (m_battery.size() - 1)] = data; + if (!m_prgram.empty()) + m_prgram[((bank * 0x2000) + offset) & (m_prgram.size() - 1)] = data; + } +} + +READ8_MEMBER(nes_sunsoft_fme7_device::read_m) +{ + UINT8 bank = m_wram_bank & 0x3f; + LOG_MMC(("Sunsoft FME7 read_m, offset: %04x\n", offset)); + + if (!(m_wram_bank & 0x40)) // is PRG ROM + return m_prg[((bank * 0x2000) + offset) & (m_prg_size - 1)]; + else if (m_wram_bank & 0x80) // is PRG RAM + { + if (!m_battery.empty()) + return m_battery[((bank * 0x2000) + offset) & (m_battery.size() - 1)]; + if (!m_prgram.empty()) + return m_prgram[((bank * 0x2000) + offset) & (m_prgram.size() - 1)]; + } + + return m_open_bus; // open bus +} + + +/*------------------------------------------------- + + Sunsoft 5B board emulation (FME7 + Sound) + + Games: Gimmick! + + iNES: mapper 69 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sunsoft_5_device::write_h) +{ + LOG_MMC(("sunsoft 5 write_h, offset %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6000) + { + case 0x4000: + m_ym2149->address_w(space, 0, data & 0x0f); + break; + case 0x6000: + m_ym2149->data_w(space, 0, data); + break; + default: + fme7_write(space, offset, data, mem_mask); + break; + } +} + +//------------------------------------------------- +// MACHINE_DRIVER( sun_5b ) +//------------------------------------------------- + +// From NESdev wiki: The 5B's audio is driven by the CPU clock (1.78977267 MHz), +// but like the NES's APU, the YM2149F has an optional clock divider which +// halves the internal clock speed. By comparison of the produced pitches +// in Gimmick! with the register values used, it appears that the 5B is a +// YM2149F operating in this mode. To use an AY-3-8910 as a substitute, +// you would need an external divider to reduce the clock speed by half. + +#define SUN5_NTSC_CLOCK (21477272.724 / 12) + +static MACHINE_CONFIG_FRAGMENT( sun_5b ) + + // additional sound hardware + MCFG_SPEAKER_STANDARD_MONO("addon") + + MCFG_SOUND_ADD("ay", YM2149, SUN5_NTSC_CLOCK/2) // divide by 2 for the internal divider + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.50) +MACHINE_CONFIG_END + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor nes_sunsoft_5_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( sun_5b ); +} diff --git a/src/devices/bus/nes/sunsoft.h b/src/devices/bus/nes/sunsoft.h new file mode 100644 index 00000000000..53ea24adeae --- /dev/null +++ b/src/devices/bus/nes/sunsoft.h @@ -0,0 +1,147 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_SUNSOFT_H +#define __NES_SUNSOFT_H + +#include "nxrom.h" +#include "sound/ay8910.h" + + +// ======================> nes_sunsoft_1_device + +class nes_sunsoft_1_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sunsoft_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); +}; + + +// ======================> nes_sunsoft_2_device + +class nes_sunsoft_2_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sunsoft_2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_sunsoft_3_device + +class nes_sunsoft_3_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sunsoft_3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + UINT16 m_irq_count; + int m_irq_enable, m_irq_toggle; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; +}; + + +// ======================> nes_sunsoft_4_device + +class nes_sunsoft_4_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sunsoft_4_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_sunsoft_4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(sun4_write); + virtual DECLARE_WRITE8_MEMBER(write_h) { sun4_write(space, offset, data, mem_mask); } + + virtual void pcb_reset(); + +protected: + void sun4_mirror(int mirror, int mirr0, int mirr1); + int m_reg, m_latch1, m_latch2, m_wram_enable; +}; + +// ======================> nes_sunsoft_fme7_device + +class nes_sunsoft_fme7_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_sunsoft_fme7_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_sunsoft_fme7_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(fme7_write); + virtual DECLARE_WRITE8_MEMBER(write_h) { fme7_write(space, offset, data, mem_mask); } + + virtual void pcb_reset(); + +private: + UINT16 m_irq_count; + int m_irq_enable; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; + + UINT8 m_latch; + UINT8 m_wram_bank; +}; + + +// ======================> nes_sunsoft_5_device + +class nes_sunsoft_5_device : public nes_sunsoft_fme7_device +{ +public: + // construction/destruction + nes_sunsoft_5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual machine_config_constructor device_mconfig_additions() const; + + virtual DECLARE_WRITE8_MEMBER(write_h); + +private: + required_device m_ym2149; +}; + + + + +// device type definition +extern const device_type NES_SUNSOFT_1; +extern const device_type NES_SUNSOFT_2; +extern const device_type NES_SUNSOFT_3; +extern const device_type NES_SUNSOFT_4; +extern const device_type NES_SUNSOFT_FME7; +extern const device_type NES_SUNSOFT_5; + +#endif diff --git a/src/devices/bus/nes/sunsoft_dcs.c b/src/devices/bus/nes/sunsoft_dcs.c new file mode 100644 index 00000000000..c7de9fd3040 --- /dev/null +++ b/src/devices/bus/nes/sunsoft_dcs.c @@ -0,0 +1,315 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Sunsoft DCS PCBs + + + Here we emulate the Sunsoft Dual Cassette System (DCS) PCB used by Nantettate!! Baseball + subslot + + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "sunsoft_dcs.h" + +#ifdef NES_PCB_DEBUG +#define VERBOSE 1 +#else +#define VERBOSE 0 +#endif + +#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0) + + +//----------------------------------------------- +// +// Nantettate!! Baseball Cartslot implementation +// +//----------------------------------------------- + +//------------------------------------------------- +// sub-cart interface +//------------------------------------------------- + +ntb_cart_interface::ntb_cart_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig, device), + m_rom(NULL) +{ +} + +ntb_cart_interface::~ntb_cart_interface() +{ +} + +//------------------------------------------------- +// sub-cart slot device +//------------------------------------------------- + +const device_type NES_NTB_SLOT = &device_creator; + +nes_ntb_slot_device::nes_ntb_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, NES_NTB_SLOT, "NES NTB Cartridge Slot", tag, owner, clock, "nes_ntb_slot", __FILE__), + device_image_interface(mconfig, *this), + device_slot_interface(mconfig, *this) +{ +} + +nes_ntb_slot_device::~nes_ntb_slot_device() +{ +} + + +void nes_ntb_slot_device::device_start() +{ + m_cart = dynamic_cast(get_card_device()); +} + +READ8_MEMBER(nes_ntb_slot_device::read) +{ + if (m_cart) + return m_cart->read(space, offset, mem_mask); + + return 0xff; +} + + +bool nes_ntb_slot_device::call_load() +{ + if (m_cart) + { + UINT8 *ROM = m_cart->get_cart_base(); + + if (!ROM) + return IMAGE_INIT_FAIL; + + if (software_entry() == NULL) + { + if (length() != 0x4000) + return IMAGE_INIT_FAIL; + + fread(&ROM, 0x4000); + } + else + { + if (get_software_region_length("rom") != 0x4000) + return IMAGE_INIT_FAIL; + + memcpy(ROM, get_software_region("rom"), 0x4000); + } + } + + return IMAGE_INIT_PASS; +} + + +bool nes_ntb_slot_device::call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry) +{ + load_software_part_region(*this, swlist, swname, start_entry ); + return TRUE; +} + +void nes_ntb_slot_device::get_default_card_software(std::string &result) +{ + software_get_default_slot(result, "ntbrom"); +} + +//----------------------------------------------- +// +// Nantettate!! Baseball Minicart implementation +// +//----------------------------------------------- + +ROM_START( ntb_rom ) + ROM_REGION(0x4000, "ntbrom", ROMREGION_ERASEFF) +ROM_END + +const device_type NES_NTB_ROM = &device_creator; + +nes_ntb_rom_device::nes_ntb_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, NES_NTB_ROM, "NES NTB ROM", tag, owner, clock, "nes_ntbrom", __FILE__), + ntb_cart_interface( mconfig, *this ) +{ +} + +void nes_ntb_rom_device::device_start() +{ + m_rom = (UINT8*)memregion("ntbrom")->base(); +} + +const rom_entry *nes_ntb_rom_device::device_rom_region() const +{ + return ROM_NAME( ntb_rom ); +} + +UINT8 *nes_ntb_rom_device::get_cart_base() +{ + return m_rom; +} + + + +//------------------------------------------------ +// +// Nantettate!! Baseball base cart implementation +// a.k.a. Sunsoft Dual Cassette System +// (variant of Sunsoft-4 PCB) +// +//------------------------------------------------ + +const device_type NES_SUNSOFT_DCS = &device_creator; + + +nes_sunsoft_dcs_device::nes_sunsoft_dcs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sunsoft_4_device(mconfig, NES_SUNSOFT_DCS, "NES Cart Sunsoft DCS PCB", tag, owner, clock, "nes_dcs", __FILE__), + m_subslot(*this, "ntb_slot") +{ +} + + +void nes_sunsoft_dcs_device::device_start() +{ + common_start(); + ntb_enable_timer = timer_alloc(TIMER_PROTECT); + ntb_enable_timer->reset(); + timer_freq = machine().device("maincpu")->cycles_to_attotime(107520); + + save_item(NAME(m_latch1)); + save_item(NAME(m_latch2)); + save_item(NAME(m_reg)); + save_item(NAME(m_wram_enable)); + save_item(NAME(m_exrom_enable)); + save_item(NAME(m_timer_on)); +} + +void nes_sunsoft_dcs_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_reg = 0; + m_latch1 = 0; + m_latch2 = 0; + m_wram_enable = 0; + m_exrom_enable = 0; + m_timer_on = 0; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Sunsoft-DCS board emulation + + Games: Nantettatte!! Baseball + + This board feature a subslot to load minicart containing + updated data? + + iNES: mapper 68 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_sunsoft_dcs_device::write_h) +{ + LOG_MMC(("Sunsoft DCS write_h, offset %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7000) + { + case 0x7000: + prg16_89ab(data & 0x07); + m_exrom_enable = !BIT(data, 3); + m_wram_enable = BIT(data, 4); + break; + default: + sun4_write(space, offset, data, mem_mask); + break; + } +} + +READ8_MEMBER(nes_sunsoft_dcs_device::read_h) +{ + LOG_MMC(("Sunsoft DCS read_h, offset: %04x\n", offset)); + + if (m_exrom_enable && m_subslot->m_cart && offset < 0x4000) + { + if (m_timer_on) + return m_subslot->m_cart->read(space, offset, mem_mask); + else + return m_open_bus; // after the timer is off, this returns open bus... + } + else + return hi_access_rom(offset); +} + +WRITE8_MEMBER(nes_sunsoft_dcs_device::write_m) +{ + LOG_MMC(("Sunsoft DCS write_m, offset: %04x, data: %02x\n", offset, data)); + + if (!m_battery.empty() && m_wram_enable) + m_battery[offset & (m_battery.size() - 1)] = data; + if (!m_prgram.empty() && m_wram_enable) + m_prgram[offset & (m_prgram.size() - 1)] = data; + if (!m_wram_enable && !m_timer_on) + { + m_timer_on = 1; + // start NTB-ROM timer + ntb_enable_timer->adjust(timer_freq); + } +} + +READ8_MEMBER(nes_sunsoft_dcs_device::read_m) +{ + LOG_MMC(("Sunsoft DCS read_m, offset: %04x\n", offset)); + + if (!m_battery.empty() && m_wram_enable) + return m_battery[offset & (m_battery.size() - 1)]; + if (!m_prgram.empty() && m_wram_enable) + return m_prgram[offset & (m_prgram.size() - 1)]; + + return m_open_bus; // open bus +} + +//------------------------------------------------- +// MACHINE_CONFIG_FRAGMENT( sub_slot ) +//------------------------------------------------- + +static SLOT_INTERFACE_START(ntb_cart) + SLOT_INTERFACE_INTERNAL("ntbrom", NES_NTB_ROM) +SLOT_INTERFACE_END + +static MACHINE_CONFIG_FRAGMENT( sub_slot ) + MCFG_NTB_MINICART_ADD("ntb_slot", ntb_cart) +MACHINE_CONFIG_END + + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor nes_sunsoft_dcs_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( sub_slot ); +} + + +//------------------------------------------------- +// device_timer - handler timer events +//------------------------------------------------- + +void nes_sunsoft_dcs_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_PROTECT) + { + m_timer_on = 0; + ntb_enable_timer->reset(); + } +} diff --git a/src/devices/bus/nes/sunsoft_dcs.h b/src/devices/bus/nes/sunsoft_dcs.h new file mode 100644 index 00000000000..67fcaf83b1e --- /dev/null +++ b/src/devices/bus/nes/sunsoft_dcs.h @@ -0,0 +1,150 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_SUNSOFT_DCS_H +#define __NES_SUNSOFT_DCS_H + +#include "sunsoft.h" + + +//----------------------------------------------- +// +// Nantettate!! Baseball Cartslot implementation +// +//----------------------------------------------- + +// ======================> ntb_cart_interface + +class ntb_cart_interface : public device_slot_card_interface +{ +public: + // construction/destruction + ntb_cart_interface(const machine_config &mconfig, device_t &device); + virtual ~ntb_cart_interface(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read) { return m_rom[offset]; } + + UINT8 *get_cart_base() { return m_rom; } + +protected: + // internal state + UINT8 *m_rom; +}; + +// ======================> nes_ntb_slot_device + +class nes_ntb_slot_device : public device_t, + public device_image_interface, + public device_slot_interface +{ +public: + // construction/destruction + nes_ntb_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual ~nes_ntb_slot_device(); + + // device-level overrides + virtual void device_start(); + virtual void device_config_complete() { update_names(); } + + // image-level overrides + virtual bool call_load(); + virtual bool call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry); + + virtual iodevice_t image_type() const { return IO_CARTSLOT; } + virtual bool is_readable() const { return 1; } + virtual bool is_writeable() const { return 0; } + virtual bool is_creatable() const { return 0; } + virtual bool must_be_loaded() const { return 0; } + virtual bool is_reset_on_load() const { return 1; } + virtual const char *image_interface() const { return "ntb_cart"; } + virtual const char *file_extensions() const { return "bin"; } + virtual const option_guide *create_option_guide() const { return NULL; } + + // slot interface overrides + virtual void get_default_card_software(std::string &result); + + virtual DECLARE_READ8_MEMBER(read); + + ntb_cart_interface* m_cart; +}; + +// device type definition +extern const device_type NES_NTB_SLOT; + + +#define MCFG_NTB_MINICART_ADD(_tag, _slot_intf) \ + MCFG_DEVICE_ADD(_tag, NES_NTB_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, NULL, false) + + +//----------------------------------------------- +// +// Nantettate!! Baseball Minicart implementation +// +//----------------------------------------------- + +// ======================> nes_ntb_rom_device + +class nes_ntb_rom_device : public device_t, + public ntb_cart_interface +{ +public: + // construction/destruction + nes_ntb_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // optional information overrides + virtual const rom_entry *device_rom_region() const; + virtual UINT8* get_cart_base(); + +protected: + // device-level overrides + virtual void device_start(); +}; + +// device type definition +extern const device_type NES_NTB_ROM; + + + +//------------------------------------------------ +// +// Nantettate!! Baseball base cart implementation +// a.k.a. Sunsoft Dual Cassette System +// (variant of Sunsoft-4 PCB) +// +//------------------------------------------------ + +// ======================> nes_sunsoft_dcs_device + +class nes_sunsoft_dcs_device : public nes_sunsoft_4_device +{ +public: + // construction/destruction + nes_sunsoft_dcs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_READ8_MEMBER(read_h); + virtual DECLARE_WRITE8_MEMBER(write_m); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +private: + int m_timer_on, m_exrom_enable; + required_device m_subslot; + + static const device_timer_id TIMER_PROTECT = 0; + emu_timer *ntb_enable_timer; + attotime timer_freq; +}; + + + +// device type definition +extern const device_type NES_SUNSOFT_DCS; + +#endif diff --git a/src/devices/bus/nes/taito.c b/src/devices/bus/nes/taito.c new file mode 100644 index 00000000000..2338d91ee25 --- /dev/null +++ b/src/devices/bus/nes/taito.c @@ -0,0 +1,470 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Taito PCBs + + + Here we emulate the following PCBs + + * Taito TC0190FMC/TC0190FMR [mapper 33] + * Taito TC0190FMC+PAL16R4 [mapper 48] + * Taito X1-005 [mapper 80, 207] + * Taito X1-017 [mapper 82] + + TODO: + - Akira does not work. Investigate why! + - Don Doko Don 2 freezes when you get to the first boss + + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "taito.h" + +#include "cpu/m6502/m6502.h" +#include "video/ppu2c0x.h" // this has to be included so that IRQ functions can access PPU_BOTTOM_VISIBLE_SCANLINE + + +#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_TC0190FMC = &device_creator; +const device_type NES_TC0190FMC_PAL16R4 = &device_creator; +const device_type NES_X1_005 = &device_creator; +const device_type NES_X1_017 = &device_creator; + + +nes_tc0190fmc_device::nes_tc0190fmc_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_tc0190fmc_device::nes_tc0190fmc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_TC0190FMC, "NES Cart Taito TC0190FMC PCB", tag, owner, clock, "nes_tc0190fmc", __FILE__) +{ +} + +nes_tc0190fmc_pal16r4_device::nes_tc0190fmc_pal16r4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_tc0190fmc_device(mconfig, NES_TC0190FMC_PAL16R4, "NES Cart Taito TC0190FMC + PAL16R4 PCB", tag, owner, clock, "nes_tc0190pal", __FILE__) +{ +} + +nes_x1_005_device::nes_x1_005_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_X1_005, "NES Cart Taito X1-005 PCB", tag, owner, clock, "nes_x1_005", __FILE__) +{ +} + +nes_x1_017_device::nes_x1_017_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_X1_017, "NES Cart Taito X1-017 PCB", tag, owner, clock, "nes_x1_017", __FILE__) +{ +} + + + +void nes_tc0190fmc_device::device_start() +{ + common_start(); +} + +void nes_tc0190fmc_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); +} + +void nes_tc0190fmc_pal16r4_device::device_start() +{ + common_start(); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); +} + +void nes_tc0190fmc_pal16r4_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_count = 0; + m_irq_count_latch = 0; +} + +void nes_x1_005_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + + save_item(NAME(m_x1_005_ram)); + + m_mapper_sram_size = 0x80; + m_mapper_sram = m_x1_005_ram; +} + +void nes_x1_005_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; +} + +void nes_x1_017_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + save_item(NAME(m_reg)); + save_item(NAME(m_mmc_vrom_bank)); + + save_item(NAME(m_x1_017_ram)); + + m_mapper_sram_size = 0x1400; + m_mapper_sram = m_x1_017_ram; +} + +void nes_x1_017_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_reg, 0, sizeof(m_reg)); + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Taito TC0190FMC board emulation + + Games: Akira, Bakushou!! Jinsei Gekijou, Don Doko Don, + Insector X, Operation Wolf, Power Blazer, Takeshi no + Sengoku Fuuunji + + iNES: mapper 33 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_tc0190fmc_device::tc0190fmc_write) +{ + LOG_MMC(("tc0190fmc_write, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7003) + { + case 0x0000: + set_nt_mirroring(BIT(data, 6) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + prg8_89(data); + break; + case 0x0001: + prg8_ab(data); + break; + case 0x0002: + chr2_0(data, CHRROM); + break; + case 0x0003: + chr2_2(data, CHRROM); + break; + case 0x2000: + chr1_4(data, CHRROM); + break; + case 0x2001: + chr1_5(data, CHRROM); + break; + case 0x2002: + chr1_6(data, CHRROM); + break; + case 0x2003: + chr1_7(data, CHRROM); + break; + } +} + +/*------------------------------------------------- + + Taito TC0190FMC + PAL16R4 board emulation + + Games: Bakushou!! Jinsei Gekijou 3, Bubble Bobble 2, + Captain Saver, Don Doko Don 2, Flintstones, Jetsons + + This is basically Mapper 33 + IRQ. Notably, IRQ works the + same as MMC3 irq, BUT latch values are "inverted" (XOR'ed + with 0xff) and there is a little delay (not implemented yet) + We simply use MMC3 IRQ and XOR the value written in the + register 0xc000 below + + iNES: mapper 48 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_tc0190fmc_pal16r4_device::hblank_irq( int scanline, int vblank, int blanked ) +{ + if (scanline < PPU_BOTTOM_VISIBLE_SCANLINE) + { + int prior_count = m_irq_count; + if (m_irq_count == 0) + m_irq_count = m_irq_count_latch; + else + m_irq_count--; + + if (m_irq_enable && !blanked && (m_irq_count == 0) && prior_count) + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); + } +} + +WRITE8_MEMBER(nes_tc0190fmc_pal16r4_device::write_h) +{ + LOG_MMC(("tc0190fmc pal16r4 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x7003) + { + case 0x0000: + prg8_89(data); + break; + case 0x0001: + case 0x0002: + case 0x0003: + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + tc0190fmc_write(space, offset, data, mem_mask); + break; + case 0x4000: + m_irq_count_latch = (0x100 - data) & 0xff; + break; + case 0x4001: + m_irq_count = m_irq_count_latch; + break; + case 0x4002: + m_irq_enable = 1; + break; + case 0x4003: + m_irq_enable = 0; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + case 0x6000: + set_nt_mirroring(BIT(data, 6) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + } +} + +/*------------------------------------------------- + + Taito X1-005 board emulation + + Actually, Fudou Myouou Den uses a variant of the board with + CIRAM, making necessary two distinct mappers & pcb_id. + + Also, we miss to emulate the security check at 0x7ef8 / 0x7ef9 + and the 0x80 ram! + + iNES: mappers 80 & 207 + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_x1_005_device::write_m) +{ + LOG_MMC(("x1_005 write_m, offset: %04x, data: %02x\n", offset, data)); + + switch (offset) + { + case 0x1ef0: + chr2_0((data & 0x7f) >> 1, CHRROM); + if (m_x1_005_alt_mirroring) + { + set_nt_page(0, CIRAM, (data & 0x80) ? 1 : 0, 1); + set_nt_page(1, CIRAM, (data & 0x80) ? 1 : 0, 1); + } + break; + case 0x1ef1: + chr2_2((data & 0x7f) >> 1, CHRROM); + if (m_x1_005_alt_mirroring) + { + set_nt_page(2, CIRAM, (data & 0x80) ? 1 : 0, 1); + set_nt_page(3, CIRAM, (data & 0x80) ? 1 : 0, 1); + } + break; + case 0x1ef2: + chr1_4(data, CHRROM); + break; + case 0x1ef3: + chr1_5(data, CHRROM); + break; + case 0x1ef4: + chr1_6(data, CHRROM); + break; + case 0x1ef5: + chr1_7(data, CHRROM); + break; + case 0x1ef6: + case 0x1ef7: + if (!m_x1_005_alt_mirroring) + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ); + break; + case 0x1ef8: + case 0x1ef9: + m_latch = data; + break; + case 0x1efa: + case 0x1efb: + prg8_89(data); + break; + case 0x1efc: + case 0x1efd: + prg8_ab(data); + break; + case 0x1efe: + case 0x1eff: + prg8_cd(data); + break; + default: + logerror("mapper80_m_w uncaught addr: %04x, value: %02x\n", offset + 0x6000, data); + break; + } + + if (offset >= 0x1f00 && m_latch == 0xa3) + m_x1_005_ram[offset & 0x7f] = data; +} + +READ8_MEMBER(nes_x1_005_device::read_m) +{ + LOG_MMC(("x1_005 read_m, offset: %04x\n", offset)); + + if (offset >= 0x1f00 && m_latch == 0xa3) + return m_x1_005_ram[offset & 0x7f]; + + return m_open_bus; // open bus +} + +/*------------------------------------------------- + + Taito X1-017 board emulation + + We miss to emulate the security check at 0x6000-0x73ff + and the ram! + + Games: Kyuukyoku Harikiri Koushien, Kyuukyoku Harikiri + Stadium, SD Keiji - Blader + + iNES: mapper 82 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_x1_017_device::set_chr() +{ + if (m_latch) + { + chr2_4(m_mmc_vrom_bank[0] >> 1, CHRROM); + chr2_6(m_mmc_vrom_bank[1] >> 1, CHRROM); + } + else + { + chr2_0(m_mmc_vrom_bank[0] >> 1, CHRROM); + chr2_2(m_mmc_vrom_bank[1] >> 1, CHRROM); + } + chr1_x(4 ^ m_latch, m_mmc_vrom_bank[2], CHRROM); + chr1_x(5 ^ m_latch, m_mmc_vrom_bank[3], CHRROM); + chr1_x(6 ^ m_latch, m_mmc_vrom_bank[4], CHRROM); + chr1_x(7 ^ m_latch, m_mmc_vrom_bank[5], CHRROM); +} + +WRITE8_MEMBER(nes_x1_017_device::write_m) +{ + LOG_MMC(("x1017 write_m, offset: %04x, data: %02x\n", offset, data)); + + switch (offset) + { + case 0x1ef0: + case 0x1ef1: + case 0x1ef2: + case 0x1ef3: + case 0x1ef4: + case 0x1ef5: + if (m_mmc_vrom_bank[offset & 0x07] != data) + { + m_mmc_vrom_bank[offset & 0x07] = data; + set_chr(); + } + break; + case 0x1ef6: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ); + if (m_latch != ((data & 0x02) << 1)) + { + m_latch = ((data & 0x02) << 1); + set_chr(); + } + break; + case 0x1ef7: + case 0x1ef8: + case 0x1ef9: + m_reg[(offset & 0x0f) - 7] = data; + break; + case 0x1efa: + prg8_89(data >> 2); + break; + case 0x1efb: + prg8_ab(data >> 2); + break; + case 0x1efc: + prg8_cd(data >> 2); + break; + default: + logerror("x1017_m_w uncaught write, addr: %04x, value: %02x\n", offset + 0x6000, data); + break; + } + + // 2+2+1 KB of Internal RAM can be independently enabled/disabled! + if (offset < 0x0800 && m_reg[0] == 0xca) + m_x1_017_ram[0x0000 + (offset & 0x7ff)] = data; + if (offset < 0x1000 && m_reg[1] == 0x69) + m_x1_017_ram[0x0800 + (offset & 0x7ff)] = data; + if (offset < 0x1400 && m_reg[2] == 0x84) + m_x1_017_ram[0x1000 + (offset & 0x3ff)] = data; +} + +READ8_MEMBER(nes_x1_017_device::read_m) +{ + LOG_MMC(("x1017 read_m, offset: %04x\n", offset)); + + // 2+2+1 KB of Internal RAM can be independently enabled/disabled! + if (offset < 0x0800 && m_reg[0] == 0xca) + return m_x1_017_ram[0x0000 + (offset & 0x7ff)]; + if (offset < 0x1000 && m_reg[1] == 0x69) + return m_x1_017_ram[0x0800 + (offset & 0x7ff)]; + if (offset < 0x1400 && m_reg[2] == 0x84) + return m_x1_017_ram[0x1000 + (offset & 0x3ff)]; + + return m_open_bus; // open bus +} diff --git a/src/devices/bus/nes/taito.h b/src/devices/bus/nes/taito.h new file mode 100644 index 00000000000..9f2b57ca512 --- /dev/null +++ b/src/devices/bus/nes/taito.h @@ -0,0 +1,104 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_TAITO_H +#define __NES_TAITO_H + +#include "nxrom.h" + + +// ======================> nes_tc0190fmc_device + +class nes_tc0190fmc_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_tc0190fmc_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_tc0190fmc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(tc0190fmc_write); + virtual DECLARE_WRITE8_MEMBER(write_h) { tc0190fmc_write(space, offset, data, mem_mask); } + + virtual void pcb_reset(); +}; + + +// ======================> nes_tc0190fmc_pal16r4_device + +class nes_tc0190fmc_pal16r4_device : public nes_tc0190fmc_device +{ +public: + // construction/destruction + nes_tc0190fmc_pal16r4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +private: + UINT16 m_irq_count, m_irq_count_latch; + int m_irq_enable; +}; + + +// ======================> nes_x1_005_device + +class nes_x1_005_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_x1_005_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); + +private: + UINT8 m_latch; + // Taito X1-005 chip contains 80 bytes of internal ram, possibly battery backed up + UINT8 m_x1_005_ram[0x80]; +}; + + +// ======================> nes_x1_017_device + +class nes_x1_017_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_x1_017_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_m); + virtual DECLARE_WRITE8_MEMBER(write_m); + + virtual void pcb_reset(); + +private: + void set_chr(); + UINT8 m_latch; + UINT8 m_reg[3]; //mapper ram protect + UINT8 m_mmc_vrom_bank[6]; + // Taito X1-017 chip contains 5K of internal ram, battery backed up + UINT8 m_x1_017_ram[0x1400]; +}; + + + + +// device type definition +extern const device_type NES_TC0190FMC; +extern const device_type NES_TC0190FMC_PAL16R4; +extern const device_type NES_X1_005; +extern const device_type NES_X1_017; + + +#endif diff --git a/src/devices/bus/nes/tengen.c b/src/devices/bus/nes/tengen.c new file mode 100644 index 00000000000..555b0da3f35 --- /dev/null +++ b/src/devices/bus/nes/tengen.c @@ -0,0 +1,376 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Tengen PCBs + + + Here we emulate the following PCBs + + * Tengen 800008 + * Tengen 800032 [mapper 64] + * Tengen 800037 [mapper 158] + + TODO: + - emulated the IRQ delay in 800032 (possibly reason of Skull & Crossbones not working?) + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "tengen.h" + +#include "cpu/m6502/m6502.h" +#include "video/ppu2c0x.h" // this has to be included so that IRQ functions can access PPU_BOTTOM_VISIBLE_SCANLINE + + +#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_TENGEN_800008 = &device_creator; +const device_type NES_TENGEN_800032 = &device_creator; +const device_type NES_TENGEN_800037 = &device_creator; + + +nes_tengen008_device::nes_tengen008_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_TENGEN_800008, "NES Cart Tengen 800008 PCB", tag, owner, clock, "nes_tengen008", __FILE__) +{ +} + +nes_tengen032_device::nes_tengen032_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_tengen032_device::nes_tengen032_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_TENGEN_800032, "NES Cart Tengen 800032 PCB", tag, owner, clock, "nes_tengen032", __FILE__) +{ +} + +nes_tengen037_device::nes_tengen037_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_tengen032_device(mconfig, NES_TENGEN_800037, "NES Cart Tengen 800037 PCB", tag, owner, clock, "nes_tengen037", __FILE__) +{ +} + + + + +void nes_tengen008_device::device_start() +{ + common_start(); +} + +void nes_tengen008_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_tengen032_device::device_start() +{ + common_start(); + irq_timer = timer_alloc(TIMER_IRQ); + irq_timer->reset(); + timer_freq = machine().device("maincpu")->cycles_to_attotime(4); + + save_item(NAME(m_mmc_prg_bank)); + save_item(NAME(m_mmc_vrom_bank)); + save_item(NAME(m_latch)); + + save_item(NAME(m_irq_mode)); + save_item(NAME(m_irq_reset)); + save_item(NAME(m_irq_enable)); + save_item(NAME(m_irq_count)); + save_item(NAME(m_irq_count_latch)); +} + +void nes_tengen032_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(m_prg_chunks - 1); + prg16_cdef(m_prg_chunks - 1); + chr8(0, m_chr_source); + memset(m_mmc_prg_bank, 0, sizeof(m_mmc_prg_bank)); + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); + + m_latch = 0; + m_irq_mode = 0; + m_irq_reset = 0; + m_irq_enable = 0; + m_irq_count = m_irq_count_latch = 0; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Tengen 800008 Board + + iNES: mapper 3? + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_tengen008_device::write_h) +{ + LOG_MMC(("tengen008 write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + prg32(data >> 3); + chr8(data, CHRROM); +} + +/*------------------------------------------------- + + Tengen 800032 Board + + Games: Klax, Road Runner, Rolling Thunder, Shinobi, Skulls + & Crossbones, Xybots + + This is very similar to MMC-3 (or more probably to the + Namcot predecessor of MMC-3), but with more registers + and with an alternative IRQ mode + + iNES: mapper 64 + + In MESS: Partially Supported (there should be a small + delay between the IRQ and its execution, but that is not + emulated yet: this is possibly the problem with Skulls + & Crossbones) + + -------------------------------------------------*/ + +inline void nes_tengen032_device::irq_clock(int blanked) +{ + // From NESdev wiki: Regardless of the mode used to clock the counter, every time the counter is clocked, + // the following actions occur: + // - If Reset reg ($C001) was written to after previous clock, reload IRQ counter with IRQ Reload + 1 + // - Otherwise, if IRQ Counter is 0, reload IRQ counter with IRQ Reload value + // - Otherwise, first decrement IRQ counter by 1, then if IRQ counter is now 0 and IRQs are enabled, + // trigger IRQ + if (m_irq_reset) + { + m_irq_reset = 0; + m_irq_count = m_irq_count_latch + 1; + } + else if (!m_irq_count) + m_irq_count = m_irq_count_latch; + + m_irq_count--; + if (m_irq_enable && !blanked && !m_irq_count) + m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE); +} + +// we use the HBLANK IRQ latch from PPU for the scanline based IRQ mode +// and a timer for the cycle based IRQ mode, which both call irq_clock + +void nes_tengen032_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + if (id == TIMER_IRQ) + { + irq_clock(0); + } +} + + +void nes_tengen032_device::hblank_irq(int scanline, int vblank, int blanked) +{ + if (!m_irq_mode) // we are in scanline mode! + { + if (scanline < PPU_BOTTOM_VISIBLE_SCANLINE) + { + irq_clock(blanked); + } + } +} + +void nes_tengen032_device::set_prg() +{ + UINT8 prg_mode = m_latch & 0x40; + + prg8_89(m_mmc_prg_bank[prg_mode ? 2: 0]); + prg8_ab(m_mmc_prg_bank[prg_mode ? 0: 1]); + prg8_cd(m_mmc_prg_bank[prg_mode ? 1: 2]); +} + +void nes_tengen032_device::chr_cb(int start, int bank, int source) +{ + chr1_x(start, bank, source); +} + +void nes_tengen032_device::set_chr() +{ + UINT8 chr_page = (m_latch & 0x80) >> 5; + + if (m_latch & 0x20) + { + chr_cb(0 ^ chr_page, m_mmc_vrom_bank[0], CHRROM); + chr_cb(1 ^ chr_page, m_mmc_vrom_bank[6], CHRROM); + chr_cb(2 ^ chr_page, m_mmc_vrom_bank[1], CHRROM); + chr_cb(3 ^ chr_page, m_mmc_vrom_bank[7], CHRROM); + } + else + { + chr_cb(0 ^ chr_page, m_mmc_vrom_bank[0] & ~0x01, CHRROM); + chr_cb(1 ^ chr_page, m_mmc_vrom_bank[0] | 0x01, CHRROM); + chr_cb(2 ^ chr_page, m_mmc_vrom_bank[1] & ~0x01, CHRROM); + chr_cb(3 ^ chr_page, m_mmc_vrom_bank[1] | 0x01, CHRROM); + } + + chr_cb(4 ^ chr_page, m_mmc_vrom_bank[2], CHRROM); + chr_cb(5 ^ chr_page, m_mmc_vrom_bank[3], CHRROM); + chr_cb(6 ^ chr_page, m_mmc_vrom_bank[4], CHRROM); + chr_cb(7 ^ chr_page, m_mmc_vrom_bank[5], CHRROM); +} + +WRITE8_MEMBER(nes_tengen032_device::tengen032_write) +{ + UINT8 helper, cmd; + LOG_MMC(("tengen032_write, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x0000: + helper = m_latch ^ data; + m_latch = data; + + // Has PRG Mode changed? + if (helper & 0x40) + set_prg(); + + // Has CHR Mode changed? + if (helper & 0xa0) + set_chr(); + break; + + case 0x0001: + cmd = m_latch & 0x0f; + switch (cmd) + { + case 0: case 1: + case 2: case 3: + case 4: case 5: + m_mmc_vrom_bank[cmd] = data; + set_chr(); + break; + case 6: case 7: + m_mmc_prg_bank[cmd - 6] = data; + set_prg(); + break; + case 8: case 9: + m_mmc_vrom_bank[cmd - 2] = data; + set_chr(); + break; + case 0x0f: + m_mmc_prg_bank[2] = data; + set_prg(); + break; + } + break; + + case 0x2000: + set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + + case 0x4000: + m_irq_count_latch = data; + break; + + case 0x4001: /* $c001 - IRQ scanline latch */ + m_irq_mode = data & 0x01; + if (m_irq_mode) + irq_timer->adjust(attotime::zero, 0, timer_freq); + else + irq_timer->adjust(attotime::never); + m_irq_reset = 1; + break; + + case 0x6000: + m_irq_enable = 0; + m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); + break; + + case 0x6001: + m_irq_enable = 1; + break; + + default: + LOG_MMC(("Tengen 800032 write. addr: %04x value: %02x\n", offset + 0x8000, data)); + break; + } +} + +/*------------------------------------------------- + + Tengen 800037 Board + + Games: Alien Syndrome + + Same as above (mapper chip RAMBO-1) but CHR A17 output + is connected to CIRAM A10, so this differs from 800032 + exactly in the same way as TLSROM differs from plain + MMC-3 + + iNES: mapper 158 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_tengen037_device::set_mirror() +{ + if (m_latch & 0x80) + { + set_nt_page(0, CIRAM, BIT(m_mmc_vrom_bank[2],7), 1); + set_nt_page(1, CIRAM, BIT(m_mmc_vrom_bank[3],7), 1); + set_nt_page(2, CIRAM, BIT(m_mmc_vrom_bank[4],7), 1); + set_nt_page(3, CIRAM, BIT(m_mmc_vrom_bank[5],7), 1); + } + else + { + set_nt_page(0, CIRAM, BIT(m_mmc_vrom_bank[0],7), 1); + set_nt_page(1, CIRAM, BIT(m_mmc_vrom_bank[0],7), 1); + set_nt_page(2, CIRAM, BIT(m_mmc_vrom_bank[1],7), 1); + set_nt_page(3, CIRAM, BIT(m_mmc_vrom_bank[1],7), 1); + } +} + +void nes_tengen037_device::chr_cb( int start, int bank, int source ) +{ + set_mirror(); // we could probably update only for one (e.g. the first) call, to slightly optimize the code + chr1_x(start, bank, source); +} + + +WRITE8_MEMBER(nes_tengen037_device::write_h) +{ + LOG_MMC(("tengen037 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x2000: + break; + + default: + tengen032_write(space, offset, data, mem_mask); + break; + } +} diff --git a/src/devices/bus/nes/tengen.h b/src/devices/bus/nes/tengen.h new file mode 100644 index 00000000000..8732e6e1321 --- /dev/null +++ b/src/devices/bus/nes/tengen.h @@ -0,0 +1,88 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_TENGEN_H +#define __NES_TENGEN_H + +#include "nxrom.h" + + +// ======================> nes_tengen008_device + +class nes_tengen008_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_tengen008_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_tengen032_device + +class nes_tengen032_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_tengen032_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_tengen032_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual DECLARE_WRITE8_MEMBER(tengen032_write); + virtual DECLARE_WRITE8_MEMBER(write_h) { tengen032_write(space, offset, data, mem_mask); } + virtual void chr_cb(int start, int bank, int source); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +protected: + void set_prg(); + void set_chr(); + void irq_clock(int blanked); + + UINT16 m_irq_count, m_irq_count_latch; + UINT8 m_irq_mode, m_irq_reset; + int m_irq_enable; + + UINT8 m_latch; + UINT8 m_mmc_prg_bank[3]; + UINT8 m_mmc_vrom_bank[8]; + + static const device_timer_id TIMER_IRQ = 0; + emu_timer *irq_timer; + attotime timer_freq; +}; + + +// ======================> nes_tengen037_device + +class nes_tengen037_device : public nes_tengen032_device +{ +public: + // construction/destruction + nes_tengen037_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void chr_cb(int start, int bank, int source); + +protected: + void set_mirror(); +}; + + + + + +// device type definition +extern const device_type NES_TENGEN_800008; +extern const device_type NES_TENGEN_800032; +extern const device_type NES_TENGEN_800037; + +#endif diff --git a/src/devices/bus/nes/txc.c b/src/devices/bus/nes/txc.c new file mode 100644 index 00000000000..ae36ece75ee --- /dev/null +++ b/src/devices/bus/nes/txc.c @@ -0,0 +1,268 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for TXC PCBs + + Here we emulate the following PCBs + + * TXC 22111 [mapper 132] + * TXC Du Ma Racing [mapper 172] + * TXC Mahjong Block [mapper 172] + * TXC Strike Wolf [mapper 36] + * TXC Commandos (and many more) [mapper 241] + + TODO: + - Does Commandos indeed use this board? + + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "txc.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_TXC_22211 = &device_creator; +const device_type NES_TXC_DUMARACING = &device_creator; +const device_type NES_TXC_MJBLOCK = &device_creator; +const device_type NES_TXC_STRIKEW = &device_creator; +const device_type NES_TXC_COMMANDOS = &device_creator; + + +nes_txc_22211_device::nes_txc_22211_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_txc_22211_device::nes_txc_22211_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_TXC_22211, "NES Cart TXC 22211 PCB", tag, owner, clock, "nes_txc_22211", __FILE__) +{ +} + +nes_txc_dumarc_device::nes_txc_dumarc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txc_22211_device(mconfig, NES_TXC_DUMARACING, "NES Cart TXC Du Ma Racing PCB", tag, owner, clock, "nes_dumarc", __FILE__) +{ +} + +nes_txc_mjblock_device::nes_txc_mjblock_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txc_22211_device(mconfig, NES_TXC_MJBLOCK, "NES Cart TXC Mahjong Block PCB", tag, owner, clock, "nes_mjblock", __FILE__) +{ +} + +nes_txc_strikew_device::nes_txc_strikew_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_TXC_STRIKEW, "NES Cart Strike Wolf PCB", tag, owner, clock, "nes_txc_strikew", __FILE__) +{ +} + +nes_txc_commandos_device::nes_txc_commandos_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_TXC_COMMANDOS, "NES Cart Commandos (and others) PCB", tag, owner, clock, "nes_txc_comm", __FILE__) +{ +} + + + + +void nes_txc_22211_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_txc_22211_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + m_reg[0] = m_reg[1] = m_reg[2] = m_reg[3] = 0; +} + +void nes_txc_strikew_device::device_start() +{ + common_start(); +} + +void nes_txc_strikew_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_txc_commandos_device::device_start() +{ + common_start(); +} + +void nes_txc_commandos_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Bootleg Board 22211 by TXC (Type A) + + Games: Creatom + + Info from NEStopia: this mapper features write to four + registers (0x4100-0x4103). The third one is used to select + PRG and CHR banks. + + iNES: mapper 132 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_txc_22211_device::write_l) +{ + LOG_MMC(("TXC 22111 write_l, offset: %04x, data: %02x\n", offset, data)); + + if (offset < 4) + m_reg[offset & 0x03] = data; +} + +READ8_MEMBER(nes_txc_22211_device::read_l) +{ + LOG_MMC(("TXC 22111 read_l, offset: %04x\n", offset)); + + if (offset == 0x0000) + return (m_reg[1] ^ m_reg[2]) | 0x40; + else + return 0x00; +} + +WRITE8_MEMBER(nes_txc_22211_device::write_h) +{ + LOG_MMC(("TXC 22111 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(m_reg[2] >> 2); + chr8(m_reg[2], CHRROM); +} + +/*------------------------------------------------- + + Bootleg Board 22211 by TXC (Type B) + + Games: 1991 Du Ma Racing + + This mapper is basically the same as Type A. Only difference is + in the way CHR banks are selected (see below) + + iNES: mapper 172 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_txc_dumarc_device::write_h) +{ + LOG_MMC(("TXC Du Ma Racing write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(m_reg[2] >> 2); + chr8((((data ^ m_reg[2]) >> 3) & 0x02) | (((data ^ m_reg[2]) >> 5) & 0x01), CHRROM); +} + +/*------------------------------------------------- + + Bootleg Board 22211 by TXC (Type C) + + Games: Mahjong Block, Xiao Ma Li + + This mapper is basically the same as 132 too. Only difference is + in 0x4100 reads which expect also bit 0 to be set + + iNES: mapper 172 + + In MESS: Supported. + + -------------------------------------------------*/ + +READ8_MEMBER(nes_txc_mjblock_device::read_l) +{ + LOG_MMC(("TXC mjblock read_l, offset: %04x\n", offset)); + + if (offset == 0x0000) + return (m_reg[1] ^ m_reg[2]) | 0x41; + else + return 0x00; +} + +/*------------------------------------------------- + + Bootleg Board 'Strike Wolf' by TXC + + Games: Strike Wolf and Policeman + + iNES: mapper 36 + + In MESS: Partially Supported (Policeman has glitches) + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_txc_strikew_device::write_h) +{ + LOG_MMC(("TXC Strike Wolf write_h, offset: %04x, data: %02x\n", offset, data)); + + // this pcb is subject to bus conflict + data = account_bus_conflict(offset, data); + + if ((offset >= 0x400) && (offset < 0x7fff)) + { + prg32(data >> 4); + chr8(data & 0x0f, CHRROM); + } +} + +/*------------------------------------------------- + + Bootleg Board MXMDHTWO by TXC + + Games: Commandos, Journey to the West, Ma Bu Mi Zhen & + Qu Wei Cheng Yu Wu, Si Lu Chuan Qi + + Simple Mapper: writes to 0x8000-0xffff sets the prg32 bank. + Not sure if returning 0x50 for reads in 0x4100-0x5000 is correct. + + iNES: mapper 241 + + In MESS: Supported. + + -------------------------------------------------*/ + +READ8_MEMBER(nes_txc_commandos_device::read_l) +{ + return 0x50; +} + +WRITE8_MEMBER(nes_txc_commandos_device::write_h) +{ + LOG_MMC(("TXC Commandos write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(data); +} diff --git a/src/devices/bus/nes/txc.h b/src/devices/bus/nes/txc.h new file mode 100644 index 00000000000..e71e6fde249 --- /dev/null +++ b/src/devices/bus/nes/txc.h @@ -0,0 +1,99 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_TXC_H +#define __NES_TXC_H + +#include "nxrom.h" + + +// ======================> nes_txc_22211_device + +class nes_txc_22211_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_txc_22211_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_txc_22211_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + UINT8 m_reg[4]; +}; + + +// ======================> nes_txc_dumarac_device + +class nes_txc_dumarc_device : public nes_txc_22211_device +{ +public: + // construction/destruction + nes_txc_dumarc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); +}; + + +// ======================> nes_txc_mjblock_device + +class nes_txc_mjblock_device : public nes_txc_22211_device +{ +public: + // construction/destruction + nes_txc_mjblock_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_READ8_MEMBER(read_l); +}; + + +// ======================> nes_txc_strikew_device + +class nes_txc_strikew_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_txc_strikew_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_txc_commandos_device + +class nes_txc_commandos_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_txc_commandos_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + + + +// device type definition +extern const device_type NES_TXC_22211; +extern const device_type NES_TXC_DUMARACING; +extern const device_type NES_TXC_MJBLOCK; +extern const device_type NES_TXC_STRIKEW; +extern const device_type NES_TXC_COMMANDOS; + +#endif diff --git a/src/devices/bus/nes/waixing.c b/src/devices/bus/nes/waixing.c new file mode 100644 index 00000000000..8a223a8e295 --- /dev/null +++ b/src/devices/bus/nes/waixing.c @@ -0,0 +1,1244 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Waixing PCBs + + Here we emulate the various PCBs used by Waixing for its games + + TODO: + - investigate the PPU issues causing some games not to have sprites (e.g. some games using mappers 15, 164, + 242, 249) + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "waixing.h" + +#include "cpu/m6502/m6502.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_WAIXING_A = &device_creator; +const device_type NES_WAIXING_A1 = &device_creator; +const device_type NES_WAIXING_B = &device_creator; +const device_type NES_WAIXING_C = &device_creator; +const device_type NES_WAIXING_D = &device_creator; +const device_type NES_WAIXING_E = &device_creator; +const device_type NES_WAIXING_F = &device_creator; +const device_type NES_WAIXING_G = &device_creator; +const device_type NES_WAIXING_H = &device_creator; +const device_type NES_WAIXING_H1 = &device_creator; +const device_type NES_WAIXING_I = &device_creator; +const device_type NES_WAIXING_J = &device_creator; +const device_type NES_WAIXING_SH2 = &device_creator; +const device_type NES_WAIXING_SEC = &device_creator; +const device_type NES_WAIXING_SGZ = &device_creator; +const device_type NES_WAIXING_SGZLZ = &device_creator; +const device_type NES_WAIXING_FFV = &device_creator; +const device_type NES_WAIXING_WXZS = &device_creator; +const device_type NES_WAIXING_DQ8 = &device_creator; +const device_type NES_WAIXING_WXZS2 = &device_creator; +const device_type NES_WAIXING_FS304 = &device_creator; + + +nes_waixing_a_device::nes_waixing_a_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_txrom_device(mconfig, type, name, tag, owner, clock, shortname, source) +{ +} + +nes_waixing_a_device::nes_waixing_a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_WAIXING_A, "NES Cart Waixing Type A PCB", tag, owner, clock, "nes_waixing_a", __FILE__) +{ +} + +nes_waixing_a1_device::nes_waixing_a1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_waixing_a_device(mconfig, NES_WAIXING_A1, "NES Cart Waixing Type A (Alt) PCB", tag, owner, clock, "nes_waixing_a1", __FILE__) +{ +} + +nes_waixing_b_device::nes_waixing_b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_waixing_a_device(mconfig, NES_WAIXING_B, "NES Cart Waixing Type B PCB", tag, owner, clock, "nes_waixing_b", __FILE__) +{ +} + +nes_waixing_c_device::nes_waixing_c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_waixing_a_device(mconfig, NES_WAIXING_C, "NES Cart Waixing Type C PCB", tag, owner, clock, "nes_waixing_c", __FILE__) +{ +} + +nes_waixing_d_device::nes_waixing_d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_waixing_a_device(mconfig, NES_WAIXING_D, "NES Cart Waixing Type D PCB", tag, owner, clock, "nes_waixing_d", __FILE__) +{ +} + +nes_waixing_e_device::nes_waixing_e_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_waixing_a_device(mconfig, NES_WAIXING_E, "NES Cart Waixing Type E PCB", tag, owner, clock, "nes_waixing_e", __FILE__) +{ +} + +nes_waixing_f_device::nes_waixing_f_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_waixing_a_device(mconfig, NES_WAIXING_F, "NES Cart Waixing Type F PCB", tag, owner, clock, "nes_waixing_f", __FILE__) +{ +} + +nes_waixing_g_device::nes_waixing_g_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_waixing_a_device(mconfig, NES_WAIXING_G, "NES Cart Waixing Type G PCB", tag, owner, clock, "nes_waixing_g", __FILE__) +{ +} + +nes_waixing_h_device::nes_waixing_h_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_txrom_device(mconfig, type, name, tag, owner, clock, shortname, source) +{ +} + +nes_waixing_h_device::nes_waixing_h_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_WAIXING_H, "NES Cart Waixing Type H PCB", tag, owner, clock, "nes_waixing_h", __FILE__) +{ +} + +nes_waixing_h1_device::nes_waixing_h1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_waixing_h_device(mconfig, NES_WAIXING_H1, "NES Cart Waixing Type H (Alt) PCB", tag, owner, clock, "nes_waixing_h1", __FILE__) +{ +} + +nes_waixing_i_device::nes_waixing_i_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_waixing_a_device(mconfig, NES_WAIXING_I, "NES Cart Waixing Type I PCB", tag, owner, clock, "nes_waixing_i", __FILE__) +{ +} + +nes_waixing_j_device::nes_waixing_j_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_waixing_a_device(mconfig, NES_WAIXING_J, "NES Cart Waixing Type J PCB", tag, owner, clock, "nes_waixing_j", __FILE__) +{ +} + +nes_waixing_sh2_device::nes_waixing_sh2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_WAIXING_SH2, "NES Cart Waixing SH2 PCB", tag, owner, clock, "nes_waixing_sh2", __FILE__) +{ +} + +nes_waixing_sec_device::nes_waixing_sec_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_txrom_device(mconfig, NES_WAIXING_SEC, "NES Cart Waixing Security Chip PCB", tag, owner, clock, "nes_waixing_sec", __FILE__) +{ +} + +nes_waixing_sgz_device::nes_waixing_sgz_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_WAIXING_SGZ, "NES Cart Waixing San Guo Zhi PCB", tag, owner, clock, "nes_waixing_sgz", __FILE__) +{ +} + +nes_waixing_sgzlz_device::nes_waixing_sgzlz_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_WAIXING_SGZLZ, "NES Cart Waixing San Guo Zhong Lie Zhuan PCB", tag, owner, clock, "nes_waixing_sgzlz", __FILE__) +{ +} + +nes_waixing_ffv_device::nes_waixing_ffv_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_WAIXING_FFV, "NES Cart Waixing Final Fantasy V PCB", tag, owner, clock, "nes_waixing_ffv", __FILE__) +{ +} + +nes_waixing_wxzs_device::nes_waixing_wxzs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_WAIXING_WXZS, "NES Cart Waixing Wai Xing Zhan Shi PCB", tag, owner, clock, "nes_waixing_wxzs", __FILE__) +{ +} + +nes_waixing_dq8_device::nes_waixing_dq8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_WAIXING_DQ8, "NES Cart Waixing Dragon Quest VIII PCB", tag, owner, clock, "nes_waixing_dq8", __FILE__) +{ +} + +nes_waixing_wxzs2_device::nes_waixing_wxzs2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_WAIXING_WXZS2, "NES Cart Waixing Wai Xing Zhan Shi 2 PCB", tag, owner, clock, "nes_waixing_wxzs2", __FILE__) +{ +} + +nes_waixing_fs304_device::nes_waixing_fs304_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_WAIXING_FS304, "NES Cart Waixing FS-304 PCB", tag, owner, clock, "nes_waixing_fs304", __FILE__) +{ +} + + +void nes_waixing_a_device::device_start() +{ + mmc3_start(); + save_item(NAME(mapper_ram)); +} + +void nes_waixing_a_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + mmc3_common_initialize(0xff, 0xff, 0); + + memset(mapper_ram, 0, sizeof(mapper_ram)); +} + +void nes_waixing_f_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + mmc3_common_initialize(0xff, 0xff, 0); + + memset(mapper_ram, 0, sizeof(mapper_ram)); + m_mmc_prg_bank[0] = 0x00; + m_mmc_prg_bank[1] = 0x01; + m_mmc_prg_bank[2] = 0x4e; + m_mmc_prg_bank[3] = 0x4f; + set_prg(m_prg_base, m_prg_mask); +} + +void nes_waixing_g_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + mmc3_common_initialize(0xff, 0xff, 0); + + memset(mapper_ram, 0, sizeof(mapper_ram)); + m_mmc_prg_bank[0] = 0x00; + m_mmc_prg_bank[1] = 0x01; + m_mmc_prg_bank[2] = 0x3e; + m_mmc_prg_bank[3] = 0x3f; + m_mmc_vrom_bank[0] = 0x00; + m_mmc_vrom_bank[1] = 0x02; + m_mmc_vrom_bank[2] = 0x04; + m_mmc_vrom_bank[3] = 0x05; + m_mmc_vrom_bank[4] = 0x06; + m_mmc_vrom_bank[5] = 0x07; + m_mmc_vrom_bank[6] = 0x01; + m_mmc_vrom_bank[7] = 0x03; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); +} + +void nes_waixing_j_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_waixing_j_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg[0] = 0x01; + m_reg[1] = 0x02; + m_reg[2] = 0x7e; + m_reg[3] = 0x7f; + mmc3_common_initialize(0xff, 0xff, 0); + set_prg(m_prg_base, m_prg_mask); +} + +void nes_waixing_sh2_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_waixing_sh2_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg[0] = m_reg[1] = 0; + mmc3_common_initialize(0xff, 0xff, 0); +} + +void nes_waixing_sec_device::device_start() +{ + mmc3_start(); + save_item(NAME(m_reg)); +} + +void nes_waixing_sec_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_reg = 0; + mmc3_common_initialize(0xff, 0xff, 0); +} + +void nes_waixing_sgz_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)); + save_item(NAME(m_mmc_vrom_bank)); +} + +void nes_waixing_sgz_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; + + memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank)); +} + +void nes_waixing_sgzlz_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); +} + +void nes_waixing_sgzlz_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + m_latch = 0; +} + +void nes_waixing_ffv_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_waixing_ffv_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg16_89ab(0); + prg16_cdef(0x1f); + chr8(0, m_chr_source); + + m_reg[0] = m_reg[1] = 0; +} + +void nes_waixing_wxzs_device::device_start() +{ + common_start(); +} + +void nes_waixing_wxzs_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_waixing_dq8_device::device_start() +{ + common_start(); +} + +void nes_waixing_dq8_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); +} + +void nes_waixing_wxzs2_device::device_start() +{ + common_start(); +} + +void nes_waixing_wxzs2_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + set_nt_mirroring(PPU_MIRROR_VERT); +} + +void nes_waixing_fs304_device::device_start() +{ + common_start(); + save_item(NAME(m_reg)); +} + +void nes_waixing_fs304_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + prg32(0); + chr8(0, m_chr_source); + + memset(m_reg, 0x00, sizeof(m_reg)); +} + + + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Waixing Board Type A + + Games: Columbus - Ougon no Yoake (C), Ji Jia Zhan Shi, + Jia A Fung Yun, Wei Luo Chuan Qi + + This mapper is quite similar to MMC3, but with two differences: + mirroring is not the same, and when VROM banks 8,9 are accessed + they point to CHRRAM and not CHRROM. + + iNES: mapper 74 + + In MESS: Supported + + -------------------------------------------------*/ + +/* MIRROR_LOW and MIRROR_HIGH are swapped! */ +void nes_waixing_a_device::set_mirror(UINT8 nt) +{ + switch (nt) + { + case 0: + case 1: + set_nt_mirroring(nt ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 2: + set_nt_mirroring(PPU_MIRROR_LOW); + break; + case 3: + set_nt_mirroring(PPU_MIRROR_HIGH); + break; + default: + LOG_MMC(("Mapper set NT to invalid value %02x", nt)); + break; + } +} + +/* Luo Ke Ren X only works with this */ +void nes_waixing_a_device::chr_cb(int start, int bank, int source) +{ + int chr_src = (bank <= 9) ? CHRRAM : CHRROM; + chr1_x(start, bank, chr_src); +} + +/* Ji Jia Zhan Shi only works with this */ +void nes_waixing_a1_device::chr_cb(int start, int bank, int source) +{ + int chr_src = ((bank == 8) || (bank == 9)) ? CHRRAM : CHRROM; + chr1_x(start, bank, chr_src); +} + +WRITE8_MEMBER(nes_waixing_a_device::waixing_write) +{ + LOG_MMC(("waixing_write, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x2000: + set_mirror(data); //maybe data & 0x03? + break; + + case 0x2001: + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } +} + +READ8_MEMBER(nes_waixing_a_device::read_l) +{ + LOG_MMC(("waixing read_l, offset: %04x\n", offset)); + offset += 0x100; + if (offset >= 0x1000 && offset < 0x1400) + return mapper_ram[offset & 0x3ff]; + else + return 0xff; +} + +WRITE8_MEMBER(nes_waixing_a_device::write_l) +{ + LOG_MMC(("waixing write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + if (offset >= 0x1000 && offset < 0x1400) + mapper_ram[offset & 0x3ff] = data; +} + + +/*------------------------------------------------- + + Waixing Board Type B + + Games: Sugoro Quest (C) + + MMC3 clone. This is a minor modification of Mapper 74, + in the sense that it is the same board except for the + CHRRAM pages. + + iNES: mapper 191 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_waixing_b_device::chr_cb(int start, int bank, int source) +{ + int chr_src = BIT(bank, 7) ? CHRRAM : CHRROM; + chr1_x(start, bank, chr_src); +} + +/*------------------------------------------------- + + Waixing Board Type C + + Games: Ying Lie Qun Xia Zhuan, Young Chivalry + + MMC3 clone. This is a minor modification of Mapper 74, + in the sense that it is the same board except for the + CHRRAM pages. + + iNES: mapper 192 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_waixing_c_device::chr_cb(int start, int bank, int source) +{ + int chr_src = ((bank == 0x08) || (bank == 0x09) || (bank == 0x0a) || (bank == 0x0b)) ? CHRRAM : CHRROM; + chr1_x(start, bank, chr_src); +} + +/*------------------------------------------------- + + Waixing Board Type D + + Games: Super Robot Taisen (C) + + MMC3 clone. This is a minor modification of Mapper 74, + in the sense that it is the same board except for the + CHRRAM pages. + + iNES: mapper 194 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_waixing_d_device::chr_cb(int start, int bank, int source) +{ + int chr_src = (bank < 0x02) ? CHRRAM : CHRROM; + chr1_x(start, bank, chr_src); +} + +/*------------------------------------------------- + + Waixing Board Type E + + Games: Captain Tsubasa Vol. II (C), Chaos World, God + Slayer (C), Zu Qiu Xiao Jiang + + MMC3 clone. This is a minor modification of Mapper 74, + in the sense that it is the same board except for the + CHRRAM pages. + + iNES: mapper 195 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_waixing_e_device::chr_cb(int start, int bank, int source) +{ + int chr_src = (bank < 0x04) ? CHRRAM : CHRROM; + chr1_x(start, bank, chr_src); +} + + +/*------------------------------------------------- + + Waixing Board Type F + + Games: Tenchi wo Kurau II (C) + + MMC3 clone. + + iNES: mapper 198 + + In MESS: Preliminary support. + + -------------------------------------------------*/ + +void nes_waixing_f_device::chr_cb(int start, int bank, int source) +{ + chr1_x(start, bank, CHRRAM); +} + +void nes_waixing_f_device::prg_cb(int start, int bank) +{ +// if (bank > 0x3f) +// bank = 0x40 | (bank & 0xf); + prg8_x(start, bank); +} + +void nes_waixing_f_device::set_prg( int prg_base, int prg_mask ) +{ + UINT8 prg_flip = (m_latch & 0x40) ? 2 : 0; + + prg_cb(0, m_mmc_prg_bank[0 ^ prg_flip]); + prg_cb(1, m_mmc_prg_bank[1]); + prg_cb(2, m_mmc_prg_bank[2 ^ prg_flip]); + prg_cb(3, m_mmc_prg_bank[3]); +} + +WRITE8_MEMBER(nes_waixing_f_device::write_h) +{ + UINT8 cmd; + LOG_MMC(("waixing_f write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x0001: + cmd = m_latch & 0x07; + switch (cmd) + { + case 0: case 1: // these do not need to be separated: we take care of them in set_chr! + case 2: case 3: case 4: case 5: + m_mmc_vrom_bank[cmd] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + case 6: + case 7: + case 8: + case 9: + m_mmc_prg_bank[cmd - 6] = data; + //printf("prg bank %d value %x\n", cmd - 6, data); + set_prg(m_prg_base, m_prg_mask); + break; + } + break; + + case 0x2001: + break; + + default: + waixing_write(space, offset, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + Waixing Board Type G + + Games: San Guo Zhi 2, Dragon Ball Z Gaiden (C), Dragon + Ball Z II (C) + + MMC3 clone, capable of switching all 4 PRG banks + + iNES: mapper 199 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_waixing_g_device::chr_cb(int start, int bank, int source) +{ + int chr_src = (bank < 0x08) ? CHRRAM : CHRROM; + chr1_x(start, bank, chr_src); +} + +void nes_waixing_g_device::set_chr(UINT8 chr, int chr_base, int chr_mask) +{ + UINT8 chr_page = (m_latch & 0x80) >> 5; + + chr_cb(chr_page ^ 0, chr_base | (m_mmc_vrom_bank[0] & chr_mask), chr); + chr_cb(chr_page ^ 1, chr_base | (m_mmc_vrom_bank[6] & chr_mask), chr); + chr_cb(chr_page ^ 2, chr_base | (m_mmc_vrom_bank[1] & chr_mask), chr); + chr_cb(chr_page ^ 3, chr_base | (m_mmc_vrom_bank[7] & chr_mask), chr); + chr_cb(chr_page ^ 4, chr_base | (m_mmc_vrom_bank[2] & chr_mask), chr); + chr_cb(chr_page ^ 5, chr_base | (m_mmc_vrom_bank[3] & chr_mask), chr); + chr_cb(chr_page ^ 6, chr_base | (m_mmc_vrom_bank[4] & chr_mask), chr); + chr_cb(chr_page ^ 7, chr_base | (m_mmc_vrom_bank[5] & chr_mask), chr); +} + +WRITE8_MEMBER(nes_waixing_g_device::write_h) +{ + UINT8 cmd; + LOG_MMC(("waixing_g write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x0001: + cmd = m_latch & 0x0f; + switch (cmd) + { + case 0: case 1: + case 2: case 3: case 4: case 5: + m_mmc_vrom_bank[cmd] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + case 6: + case 7: + case 8: + case 9: + m_mmc_prg_bank[cmd - 6] = data; + set_prg(m_prg_base, m_prg_mask); + break; + case 0x0a: case 0x0b: + m_mmc_vrom_bank[cmd - 4] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + } + break; + + default: + waixing_write(space, offset, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + Waixing Board Type H + + Games: Ying Xiong Yuan Yi Jing Chuan Qi, Yong Zhe Dou E + Long - Dragon Quest VII + + MMC3 clone. More info to come. + + Notice that Chinese Zelda translation stops working if + WRAM protect bit is ignored (i.e. if writes to 0x2001 + are skipped)! OTOH, Chinese Monster Maker 1/2 translations + and Zheng Ba ShiJi stop working if WRAM protect is + accounted. So we split the board into two subtypes + + iNES: mapper 245 + + In MESS: Supported. + + -------------------------------------------------*/ + +void nes_waixing_h_device::chr_cb(int start, int bank, int source) +{ + if (source == CHRROM) + chr1_x(start, bank, source); +} + +WRITE8_MEMBER(nes_waixing_h_device::write_h) +{ + UINT8 cmd; + LOG_MMC(("waixing_h write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x0001: + cmd = m_latch & 0x07; + switch (cmd) + { + case 0: // in this case we set prg_base in addition to m_mmc_vrom_bank! + m_prg_base = (data << 5) & 0x40; + m_prg_mask = 0x3f; + set_prg(m_prg_base, m_prg_mask); + case 1: + case 2: case 3: case 4: case 5: + m_mmc_vrom_bank[cmd] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + case 6: + case 7: + m_mmc_prg_bank[cmd - 6] = data; + set_prg(m_prg_base, m_prg_mask); + break; + case 0x0a: case 0x0b: // CHR switches in 1K banks only, and bank1 & bank3 are controlled by these + m_mmc_vrom_bank[cmd - 4] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + } + break; + + case 0x2001: + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } +} + +WRITE8_MEMBER(nes_waixing_h1_device::write_h) +{ + UINT8 cmd; + LOG_MMC(("waixing_h1 write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x0001: + cmd = m_latch & 0x07; + switch (cmd) + { + case 0: // in this case we set prg_base in addition to m_mmc_vrom_bank! + m_prg_base = (data << 5) & 0x40; + m_prg_mask = 0x3f; + set_prg(m_prg_base, m_prg_mask); + case 1: + case 2: case 3: case 4: case 5: + m_mmc_vrom_bank[cmd] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + case 6: + case 7: + m_mmc_prg_bank[cmd - 6] = data; + set_prg(m_prg_base, m_prg_mask); + break; + case 0x0a: case 0x0b: // CHR switches in 1K banks only, and bank1 & bank3 are controlled by these + m_mmc_vrom_bank[cmd - 4] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + } + break; + + default: + txrom_write(space, offset, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + Waixing Board Type J + + Games: Final Fantasy III (C) + + MMC3 clone. + + In MESS: Preliminary support. + + -------------------------------------------------*/ + +void nes_waixing_j_device::set_prg( int prg_base, int prg_mask ) +{ + UINT8 prg_flip = (m_latch & 0x40) ? 2 : 0; + + prg_cb(0, m_reg[0 ^ prg_flip]); + prg_cb(1, m_reg[1]); + prg_cb(2, m_reg[2 ^ prg_flip]); + prg_cb(3, m_reg[3]); +} + +WRITE8_MEMBER(nes_waixing_j_device::write_h) +{ + UINT8 cmd; + LOG_MMC(("waixing_f write_h, offset: %04x, data: %02x\n", offset, data)); + + switch (offset & 0x6001) + { + case 0x0001: + cmd = m_latch & 0x07; + switch (cmd) + { + case 0: case 1: // these do not need to be separated: we take care of them in set_chr! + case 2: case 3: case 4: case 5: + m_mmc_vrom_bank[cmd] = data; + set_chr(m_chr_source, m_chr_base, m_chr_mask); + break; + case 6: + case 7: + case 8: + case 9: + m_reg[cmd - 6] = data; + set_prg(m_prg_base, m_prg_mask); + break; + } + break; + +// case 0x2001: +// break; + + default: + waixing_write(space, offset, data, mem_mask); + break; + } +} + +/*------------------------------------------------- + + Waixing SH2 Board + + Games: Fire Emblem (C) and Fire Emblem Gaiden (C) + + MMC3 clone with different access to CHR + + iNES: mapper 165 + + In MESS: Partially Supported. + + -------------------------------------------------*/ + +void nes_waixing_sh2_device::chr_cb(int start, int bank, int source) +{ + chr4_0(m_reg[0], m_reg[0] ? CHRRAM : CHRROM); + chr4_4(m_reg[1], m_reg[1] ? CHRRAM : CHRROM); +} + +READ8_MEMBER(nes_waixing_sh2_device::chr_r) +{ + int bank = offset >> 10; + UINT8 val = m_chr_access[bank][offset & 0x3ff]; // this would be usual return value + int chr_helper; + + switch (offset & 0xff8) + { + case 0xfd0: chr_helper = (bank & 0x4) | 0x0; break; + case 0xfe8: chr_helper = (bank & 0x4) | 0x2; break; + default: return val; + } + + m_reg[offset >> 12] = chr_helper; + if (offset & 0x1000) + chr4_4(m_reg[1], m_reg[1] ? CHRRAM : CHRROM); + else + chr4_0(m_reg[0], m_reg[0] ? CHRRAM : CHRROM); + + return val; +} + +/*------------------------------------------------- + + Waixing Board with Security Chip + + Games: Duo Bao Xiao Ying Hao - Guang Ming yu An Hei Chuan Shuo, + Myth Struggle, San Shi Liu Ji, Shui Hu Zhuan + + MMC3 clone + + iNES: mapper 249 + + In MESS: Partially Supported. + + -------------------------------------------------*/ + +void nes_waixing_sec_device::prg_cb(int start, int bank) +{ + if (m_reg) + bank = BITSWAP8(bank & 0x1f,7,6,5,2,1,3,4,0); + + prg8_x(start, bank); +} + +void nes_waixing_sec_device::chr_cb(int start, int bank, int source) +{ + if (m_reg) + bank = BITSWAP8(bank, 5,4,2,6,7,3,1,0); + + chr1_x(start, bank, source); +} + +WRITE8_MEMBER(nes_waixing_sec_device::write_l) +{ + LOG_MMC(("waixing_sec write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; + + if (offset == 0x1000) + { + m_reg = data & 0x02; + set_prg(m_prg_base, m_prg_mask); + set_chr(m_chr_source, m_chr_base, m_chr_mask); + } +} + +/*------------------------------------------------- + + Waixing San Guo Zhi Board + + Games: San Guo Zhi + + This board uses Konami IRQ + + iNES: mapper 252 + + In MESS: Unsupported. + + -------------------------------------------------*/ + +// same as Konami IRQ +void nes_waixing_sgz_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; + m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE); + } +} + +WRITE8_MEMBER(nes_waixing_sgz_device::write_h) +{ + UINT8 helper, bank; + LOG_MMC(("waixing_sgz 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 0x3000: + case 0x4000: + case 0x5000: + case 0x6000: + bank = ((offset & 0x7000) - 0x3000) / 0x0800 + ((offset & 0x0008) >> 3); + helper = offset & 0x04; + if (helper) + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4); + else + m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | (data & 0x0f); + chr1_x(bank, m_mmc_vrom_bank[bank], CHRROM); + break; + case 0x7000: + switch (offset & 0x0c) + { + case 0x00: + m_irq_count_latch = (m_irq_count_latch & 0xf0) | (data & 0x0f); + break; + case 0x04: + m_irq_count_latch = (m_irq_count_latch & 0x0f) | ((data & 0x0f) << 4); + break; + case 0x08: + m_irq_enable = data & 0x02; + m_irq_count_latch = data & 0x01; + if (data & 0x02) + m_irq_count = m_irq_count_latch; + break; + case 0x0c: + m_irq_enable = m_irq_enable_latch; + break; + } + break; + } +} + + +/*------------------------------------------------- + + Waixing San Guo Zhong Lie Zhuan Board + + Games: Fan Kong Jing Ying, San Guo Zhong Lie Zhuan, Xing + Ji Zheng Ba + + iNES: mapper 178 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_waixing_sgzlz_device::write_l) +{ + LOG_MMC(("waixing_sgzlz write_l, offset: %04x, data: %02x\n", offset, data)); + + switch (offset) + { + case 0x700: + set_nt_mirroring(data ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 0x701: + m_latch = (m_latch & 0x0c) | ((data >> 1) & 0x03); + prg32(m_latch); + break; + case 0x702: + m_latch = (m_latch & 0x03) | ((data << 2) & 0x0c); + break; + } +} + +/*------------------------------------------------- + + Waixing Final Fantasy V Board + + Games: Darkseed, Digital Dragon, Final Fantasy V, Pocket + Monster Red + + iNES: mapper 164 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_waixing_ffv_device::write_l) +{ + UINT8 helper; + LOG_MMC(("waixing_ffv write_l, offset: %04x, data: %02x\n", offset, data)); + offset += 0x100; /* the checks work better on addresses */ + + if ((offset & 0x1200) == 0x1000) + { + m_reg[BIT(offset, 8)] = data; + helper = BIT(m_reg[1], 0) << 5; + switch (m_reg[0] & 0x70) + { + case 0x00: + case 0x20: + case 0x40: + case 0x60: + prg16_89ab(helper | ((m_reg[0] >> 1) & 0x10) | (m_reg[0] & 0x0f)); + prg16_cdef(helper & 0x1f); + break; + case 0x50: + prg32((helper >> 1) | (m_reg[0] & 0x0f)); + break; + case 0x70: + prg16_89ab(helper | ((m_reg[0] << 1) & 0x10) | (m_reg[0] & 0x0f)); + prg16_cdef(helper & 0x1f); + break; + } + } +} + +/*------------------------------------------------- + + Waixing Zhan Shi Board + + Games: Wai Xing Zhan Shi + + Simple mapper: writes to 0x8000-0xffff sets prg32 banks to + (offset>>3)&f. written data&3 sets the mirroring (with + switched high/low compared to the standard one). + + A crc check is required to support Dragon Quest VIII (which + uses a slightly different board) + + iNES: mapper 242 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_waixing_wxzs_device::write_h) +{ + LOG_MMC(("waixing_zs write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(offset >> 3); + + switch (data & 0x03) + { + case 0: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 1: set_nt_mirroring(PPU_MIRROR_HORZ); break; + case 2: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 3: set_nt_mirroring(PPU_MIRROR_HIGH); break; + } +} + +/*------------------------------------------------- + + Waixing Dragon Quest VIII Board + + Games: Dragon Quest VIII + + Simple mapper: writes to 0x8000-0xffff sets prg32 banks to + (offset>>3)&f. + + iNES: mapper 242 + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_waixing_dq8_device::write_h) +{ + LOG_MMC(("waixing_dq8 write_h, offset: %04x, data: %02x\n", offset, data)); + + prg32(offset >> 3); +} + + +/*------------------------------------------------- + + Waixing WXZS2 / PS2 board + + Games: Wai Xing Zhan Shi 2 (aka Phantasy Star 2), + Bao Xiao Tien Guo, Bio Hazard, Pokemon Gold, Subor (R) + + iNES: mapper 15 + + In MESS: Supported + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_waixing_wxzs2_device::write_h) +{ + UINT8 flip = (data & 0x80) >> 7; + UINT8 helper = (data & 0x7f) << 1; + + LOG_MMC(("waixing_wxzs2 write_h, offset: %04x, data: %02x\n", offset, data)); + + set_nt_mirroring(BIT(data, 6) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + + switch (offset & 0x0fff) + { + case 0x000: + prg8_89((helper + 0) ^ flip); + prg8_ab((helper + 1) ^ flip); + prg8_cd((helper + 2) ^ flip); + prg8_ef((helper + 3) ^ flip); + break; + case 0x001: + helper |= flip; + prg8_89(helper); + prg8_ab(helper + 1); + prg8_cd(helper + 1); + prg8_ef(helper + 1); + break; + case 0x002: + helper |= flip; + prg8_89(helper); + prg8_ab(helper); + prg8_cd(helper); + prg8_ef(helper); + break; + case 0x003: + helper |= flip; + prg8_89(helper); + prg8_ab(helper + 1); + prg8_cd(helper); + prg8_ef(helper + 1); + break; + } +} + +/*------------------------------------------------- + + Board UNL-FS304 + + Games: A Link to the Past by Waixing + + iNES: mapper 162? (only found in UNIF format) + + In MESS: Supported. + + -------------------------------------------------*/ + +WRITE8_MEMBER(nes_waixing_fs304_device::write_l) +{ + LOG_MMC(("fs304 write_l, offset: %04x, data: %02x\n", offset, data)); + int bank; + offset += 0x100; + + if (offset >= 0x1000) + { + m_reg[(offset >> 8) & 3] = data; + bank = ((m_reg[2] & 0x0f) << 4) | BIT(m_reg[1], 1) | (m_reg[0] & 0x0e); + prg32(bank); + chr8(0, CHRRAM); + } +} diff --git a/src/devices/bus/nes/waixing.h b/src/devices/bus/nes/waixing.h new file mode 100644 index 00000000000..42220c5523d --- /dev/null +++ b/src/devices/bus/nes/waixing.h @@ -0,0 +1,396 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +#ifndef __NES_WAIXING_H +#define __NES_WAIXING_H + +#include "mmc3.h" + + +// ======================> nes_waixing_a_device + +class nes_waixing_a_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_waixing_a_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_waixing_a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(read_l); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual DECLARE_WRITE8_MEMBER(waixing_write); + virtual DECLARE_WRITE8_MEMBER(write_h) { waixing_write(space, offset, data, mem_mask); } + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +protected: + void set_mirror(UINT8 nt); + UINT8 mapper_ram[0x400]; +}; + + +// ======================> nes_waixing_a1_device + +class nes_waixing_a1_device : public nes_waixing_a_device +{ +public: + // construction/destruction + nes_waixing_a1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual void chr_cb(int start, int bank, int source); +}; + + +// ======================> nes_waixing_b_device + +class nes_waixing_b_device : public nes_waixing_a_device +{ +public: + // construction/destruction + nes_waixing_b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void chr_cb(int start, int bank, int source); +}; + + +// ======================> nes_waixing_c_device + +class nes_waixing_c_device : public nes_waixing_a_device +{ +public: + // construction/destruction + nes_waixing_c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void chr_cb(int start, int bank, int source); +}; + + +// ======================> nes_waixing_d_device + +class nes_waixing_d_device : public nes_waixing_a_device +{ +public: + // construction/destruction + nes_waixing_d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void chr_cb(int start, int bank, int source); +}; + + +// ======================> nes_waixing_e_device + +class nes_waixing_e_device : public nes_waixing_a_device +{ +public: + // construction/destruction + nes_waixing_e_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void chr_cb(int start, int bank, int source); +}; + + +// ======================> nes_waixing_f_device + +class nes_waixing_f_device : public nes_waixing_a_device +{ +public: + // construction/destruction + nes_waixing_f_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); +private: + virtual void set_prg(int prg_base, int prg_mask); +}; + + +// ======================> nes_waixing_g_device + +class nes_waixing_g_device : public nes_waixing_a_device +{ +public: + // construction/destruction + nes_waixing_g_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +protected: + virtual void set_chr(UINT8 chr, int chr_base, int chr_mask); +}; + + +// ======================> nes_waixing_h_device + +class nes_waixing_h_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_waixing_h_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_waixing_h_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); + virtual void chr_cb(int start, int bank, int source); + + // This PCB does not have 1K of internal RAM, so it's not derived from nes_waixing_a_device!! +}; + + +// ======================> nes_waixing_h1_device + +class nes_waixing_h1_device : public nes_waixing_h_device +{ +public: + // construction/destruction + nes_waixing_h1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual DECLARE_WRITE8_MEMBER(write_h); + + // This variant does not ignore the wram protect! +}; + + +// ======================> nes_waixing_i_device + +class nes_waixing_i_device : public nes_waixing_a_device +{ +public: + // construction/destruction + nes_waixing_i_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // still to emulate this variant +}; + + +// ======================> nes_waixing_j_device + +class nes_waixing_j_device : public nes_waixing_a_device +{ +public: + // construction/destruction + nes_waixing_j_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); + +protected: + virtual void set_prg(int prg_base, int prg_mask); + UINT8 m_reg[4]; +}; + + +// ======================> nes_waixing_sh2_device + +class nes_waixing_sh2_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_waixing_sh2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_READ8_MEMBER(chr_r); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +protected: + UINT8 m_reg[2]; +}; + + +// ======================> nes_waixing_sec_device + +class nes_waixing_sec_device : public nes_txrom_device +{ +public: + // construction/destruction + nes_waixing_sec_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + virtual void prg_cb(int start, int bank); + virtual void chr_cb(int start, int bank, int source); + + virtual void pcb_reset(); + +protected: + UINT8 m_reg; +}; + + +// ======================> nes_waixing_sgz_device + +class nes_waixing_sgz_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_waixing_sgz_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void hblank_irq(int scanline, int vblank, int blanked); + virtual void pcb_reset(); + +protected: + UINT16 m_irq_count, m_irq_count_latch; + int m_irq_enable, m_irq_enable_latch; + + UINT8 m_mmc_vrom_bank[8]; +}; + + +// ======================> nes_waixing_sgzlz_device + +class nes_waixing_sgzlz_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_waixing_sgzlz_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); + +protected: + UINT8 m_latch; +}; + + +// ======================> nes_waixing_ffv_device + +class nes_waixing_ffv_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_waixing_ffv_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); + +protected: + UINT8 m_reg[2]; +}; + + +// ======================> nes_waixing_wxzs_device + +class nes_waixing_wxzs_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_waixing_wxzs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_waixing_dq8_device + +class nes_waixing_dq8_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_waixing_dq8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_waixing_wxzs2_device + +class nes_waixing_wxzs2_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_waixing_wxzs2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_h); + + virtual void pcb_reset(); +}; + + +// ======================> nes_waixing_fs304_device + +class nes_waixing_fs304_device : public nes_nrom_device +{ +public: + // construction/destruction + nes_waixing_fs304_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual DECLARE_WRITE8_MEMBER(write_l); + + virtual void pcb_reset(); + +protected: + UINT8 m_reg[4]; +}; + + + + + +// device type definition +extern const device_type NES_WAIXING_A; +extern const device_type NES_WAIXING_A1; +extern const device_type NES_WAIXING_B; +extern const device_type NES_WAIXING_C; +extern const device_type NES_WAIXING_D; +extern const device_type NES_WAIXING_E; +extern const device_type NES_WAIXING_F; +extern const device_type NES_WAIXING_G; +extern const device_type NES_WAIXING_H; +extern const device_type NES_WAIXING_H1; +extern const device_type NES_WAIXING_I; +extern const device_type NES_WAIXING_J; +extern const device_type NES_WAIXING_SH2; +extern const device_type NES_WAIXING_SEC; +extern const device_type NES_WAIXING_SGZ; +extern const device_type NES_WAIXING_SGZLZ; +extern const device_type NES_WAIXING_FFV; +extern const device_type NES_WAIXING_WXZS; +extern const device_type NES_WAIXING_DQ8; +extern const device_type NES_WAIXING_WXZS2; +extern const device_type NES_WAIXING_FS304; + + +#endif -- cgit v1.2.3-70-g09d2