summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/nes
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2015-09-13 08:41:44 +0200
committer Miodrag Milanovic <mmicko@gmail.com>2015-09-13 08:41:44 +0200
commitf88cefad27a1737c76e09d99c9fb43e173506081 (patch)
tree2d8167d03579c46e226471747eb4407bd00ed6fa /src/devices/bus/nes
parente92ac9e0fa8e99869894bea00589bbb526be30aa (diff)
Move all devices into separate part of src tree (nw)
Diffstat (limited to 'src/devices/bus/nes')
-rw-r--r--src/devices/bus/nes/2a03pur.c109
-rw-r--r--src/devices/bus/nes/2a03pur.h33
-rw-r--r--src/devices/bus/nes/act53.c206
-rw-r--r--src/devices/bus/nes/act53.h38
-rw-r--r--src/devices/bus/nes/aladdin.c339
-rw-r--r--src/devices/bus/nes/aladdin.h167
-rw-r--r--src/devices/bus/nes/ave.c214
-rw-r--r--src/devices/bus/nes/ave.h69
-rw-r--r--src/devices/bus/nes/bandai.c479
-rw-r--r--src/devices/bus/nes/bandai.h141
-rw-r--r--src/devices/bus/nes/benshieng.c105
-rw-r--r--src/devices/bus/nes/benshieng.h34
-rw-r--r--src/devices/bus/nes/bootleg.c1635
-rw-r--r--src/devices/bus/nes/bootleg.h480
-rw-r--r--src/devices/bus/nes/camerica.c225
-rw-r--r--src/devices/bus/nes/camerica.h72
-rw-r--r--src/devices/bus/nes/cne.c220
-rw-r--r--src/devices/bus/nes/cne.h66
-rw-r--r--src/devices/bus/nes/cony.c397
-rw-r--r--src/devices/bus/nes/cony.h75
-rw-r--r--src/devices/bus/nes/datach.c420
-rw-r--r--src/devices/bus/nes/datach.h170
-rw-r--r--src/devices/bus/nes/discrete.c209
-rw-r--r--src/devices/bus/nes/discrete.h80
-rw-r--r--src/devices/bus/nes/disksys.c431
-rw-r--r--src/devices/bus/nes/disksys.h71
-rw-r--r--src/devices/bus/nes/event.c239
-rw-r--r--src/devices/bus/nes/event.h44
-rw-r--r--src/devices/bus/nes/ggenie.c274
-rw-r--r--src/devices/bus/nes/ggenie.h58
-rw-r--r--src/devices/bus/nes/henggedianzi.c278
-rw-r--r--src/devices/bus/nes/henggedianzi.h78
-rw-r--r--src/devices/bus/nes/hes.c87
-rw-r--r--src/devices/bus/nes/hes.h28
-rw-r--r--src/devices/bus/nes/hosenkan.c155
-rw-r--r--src/devices/bus/nes/hosenkan.h39
-rw-r--r--src/devices/bus/nes/irem.c352
-rw-r--r--src/devices/bus/nes/irem.h108
-rw-r--r--src/devices/bus/nes/jaleco.c886
-rw-r--r--src/devices/bus/nes/jaleco.h256
-rw-r--r--src/devices/bus/nes/jy.c589
-rw-r--r--src/devices/bus/nes/jy.h104
-rw-r--r--src/devices/bus/nes/kaiser.c588
-rw-r--r--src/devices/bus/nes/kaiser.h184
-rw-r--r--src/devices/bus/nes/karastudio.c309
-rw-r--r--src/devices/bus/nes/karastudio.h147
-rw-r--r--src/devices/bus/nes/konami.c787
-rw-r--r--src/devices/bus/nes/konami.h155
-rw-r--r--src/devices/bus/nes/legacy.c311
-rw-r--r--src/devices/bus/nes/legacy.h81
-rw-r--r--src/devices/bus/nes/mmc1.c374
-rw-r--r--src/devices/bus/nes/mmc1.h82
-rw-r--r--src/devices/bus/nes/mmc2.c198
-rw-r--r--src/devices/bus/nes/mmc2.h54
-rw-r--r--src/devices/bus/nes/mmc3.c560
-rw-r--r--src/devices/bus/nes/mmc3.h146
-rw-r--r--src/devices/bus/nes/mmc3_clones.c2588
-rw-r--r--src/devices/bus/nes/mmc3_clones.h697
-rw-r--r--src/devices/bus/nes/mmc5.c679
-rw-r--r--src/devices/bus/nes/mmc5.h94
-rw-r--r--src/devices/bus/nes/multigame.c2602
-rw-r--r--src/devices/bus/nes/multigame.h894
-rw-r--r--src/devices/bus/nes/namcot.c688
-rw-r--r--src/devices/bus/nes/namcot.h163
-rw-r--r--src/devices/bus/nes/nanjing.c175
-rw-r--r--src/devices/bus/nes/nanjing.h38
-rw-r--r--src/devices/bus/nes/nes_carts.c366
-rw-r--r--src/devices/bus/nes/nes_carts.h73
-rw-r--r--src/devices/bus/nes/nes_ines.inc939
-rw-r--r--src/devices/bus/nes/nes_pcb.inc609
-rw-r--r--src/devices/bus/nes/nes_slot.c1053
-rw-r--r--src/devices/bus/nes/nes_slot.h430
-rw-r--r--src/devices/bus/nes/nes_unif.inc575
-rw-r--r--src/devices/bus/nes/ntdec.c186
-rw-r--r--src/devices/bus/nes/ntdec.h51
-rw-r--r--src/devices/bus/nes/nxrom.c583
-rw-r--r--src/devices/bus/nes/nxrom.h216
-rw-r--r--src/devices/bus/nes/pirate.c1387
-rw-r--r--src/devices/bus/nes/pirate.h399
-rw-r--r--src/devices/bus/nes/pt554.c107
-rw-r--r--src/devices/bus/nes/pt554.h31
-rw-r--r--src/devices/bus/nes/racermate.c92
-rw-r--r--src/devices/bus/nes/racermate.h35
-rw-r--r--src/devices/bus/nes/rcm.c303
-rw-r--r--src/devices/bus/nes/rcm.h105
-rw-r--r--src/devices/bus/nes/rexsoft.c262
-rw-r--r--src/devices/bus/nes/rexsoft.h61
-rw-r--r--src/devices/bus/nes/sachen.c695
-rw-r--r--src/devices/bus/nes/sachen.h261
-rw-r--r--src/devices/bus/nes/somari.c421
-rw-r--r--src/devices/bus/nes/somari.h54
-rw-r--r--src/devices/bus/nes/sunsoft.c628
-rw-r--r--src/devices/bus/nes/sunsoft.h147
-rw-r--r--src/devices/bus/nes/sunsoft_dcs.c315
-rw-r--r--src/devices/bus/nes/sunsoft_dcs.h150
-rw-r--r--src/devices/bus/nes/taito.c470
-rw-r--r--src/devices/bus/nes/taito.h104
-rw-r--r--src/devices/bus/nes/tengen.c376
-rw-r--r--src/devices/bus/nes/tengen.h88
-rw-r--r--src/devices/bus/nes/txc.c268
-rw-r--r--src/devices/bus/nes/txc.h99
-rw-r--r--src/devices/bus/nes/waixing.c1244
-rw-r--r--src/devices/bus/nes/waixing.h396
103 files changed, 36233 insertions, 0 deletions
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::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::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::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<aladdin_cart_interface *>(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<nes_algn_rom_device>;
+const device_type NES_ALGQ_ROM = &device_creator<nes_algq_rom_device>;
+
+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::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<nes_aladdin_slot_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<nes_nina001_device>;
+const device_type NES_NINA006 = &device_creator<nes_nina006_device>;
+const device_type NES_MAXI15 = &device_creator<nes_maxi15_device>;
+
+
+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<nes_oekakids_device>;
+const device_type NES_FCG = &device_creator<nes_fcg_device>;
+const device_type NES_LZ93D50 = &device_creator<nes_lz93d50_device>;
+const device_type NES_LZ93D50_24C01 = &device_creator<nes_lz93d50_24c01_device>;
+const device_type NES_LZ93D50_24C02 = &device_creator<nes_lz93d50_24c02_device>;
+const device_type NES_FJUMP2 = &device_creator<nes_fjump2_device>;
+
+
+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<cpu_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<cpu_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<cpu_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<i2cmem_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::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<nes_ax5705_device>;
+const device_type NES_SC127 = &device_creator<nes_sc127_device>;
+const device_type NES_MARIOBABY = &device_creator<nes_mbaby_device>;
+const device_type NES_ASN = &device_creator<nes_asn_device>;
+const device_type NES_SMB3PIRATE = &device_creator<nes_smb3p_device>;
+const device_type NES_BTL_DNINJA = &device_creator<nes_btl_dn_device>;
+const device_type NES_WHIRLWIND_2706 = &device_creator<nes_whirl2706_device>;
+const device_type NES_SMB2J = &device_creator<nes_smb2j_device>;
+const device_type NES_SMB2JA = &device_creator<nes_smb2ja_device>;
+const device_type NES_SMB2JB = &device_creator<nes_smb2jb_device>;
+const device_type NES_09034A = &device_creator<nes_09034a_device>;
+const device_type NES_TOBIDASE = &device_creator<nes_tobidase_device>;
+const device_type NES_LH32 = &device_creator<nes_lh32_device>;
+const device_type NES_LH10 = &device_creator<nes_lh10_device>;
+const device_type NES_LH53 = &device_creator<nes_lh53_device>;
+const device_type NES_2708 = &device_creator<nes_2708_device>;
+const device_type NES_AC08 = &device_creator<nes_ac08_device>;
+const device_type NES_UNL_BB = &device_creator<nes_unl_bb_device>;
+const device_type NES_MMALEE = &device_creator<nes_mmalee_device>;
+const device_type NES_SHUIGUAN = &device_creator<nes_shuiguan_device>;
+
+
+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<cpu_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<cpu_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<cpu_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<cpu_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<cpu_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<cpu_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<cpu_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<nes_bf9093_device>;
+const device_type NES_BF9096 = &device_creator<nes_bf9096_device>;
+const device_type NES_GOLDEN5 = &device_creator<nes_golden5_device>;
+
+
+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<nes_cne_decathl_device>;
+const device_type NES_CNE_FSB = &device_creator<nes_cne_fsb_device>;
+const device_type NES_CNE_SHLZ = &device_creator<nes_cne_shlz_device>;
+
+
+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<nes_cony_device>;
+const device_type NES_YOKO = &device_creator<nes_yoko_device>;
+
+
+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<cpu_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<cpu_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::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<datach_cart_interface *>(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<nes_datach_rom_device>;
+const device_type NES_DATACH_24C01 = &device_creator<nes_datach_24c01_device>;
+
+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::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<cpu_device>("maincpu")->cycles_to_attotime(1));
+ serial_timer->adjust(attotime::zero, 0, machine().device<cpu_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<i2cmem_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<i2cmem_device> m_i2cmem;
+ required_device<barcode_reader_device> m_reader;
+ required_device<nes_datach_slot_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<nes_74x161x161x32_device>;
+const device_type NES_74X139X74 = &device_creator<nes_74x139x74_device>;
+const device_type NES_74X377 = &device_creator<nes_74x377_device>;
+const device_type NES_74X161X138 = &device_creator<nes_74x161x138_device>;
+
+
+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<nes_disksys_device *>(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<nes_disksys_device *>(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::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<cpu_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<legacy_floppy_image_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::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<cpu_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::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<nes_cart_slot_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<nes_hengg_srich_device>;
+const device_type NES_HENGG_XHZS = &device_creator<nes_hengg_xhzs_device>;
+const device_type NES_HENGG_SHJY3 = &device_creator<nes_hengg_shjy3_device>;
+
+
+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::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::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<nes_lrog017_device>;
+const device_type NES_HOLYDIVR = &device_creator<nes_holydivr_device>;
+const device_type NES_TAM_S1 = &device_creator<nes_tam_s1_device>;
+const device_type NES_G101 = &device_creator<nes_g101_device>;
+const device_type NES_H3001 = &device_creator<nes_h3001_device>;
+
+
+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<cpu_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<nes_jf11_device>;
+const device_type NES_JF16 = &device_creator<nes_jf16_device>;
+const device_type NES_JF17 = &device_creator<nes_jf17_device>;
+const device_type NES_JF19 = &device_creator<nes_jf19_device>;
+const device_type NES_SS88006 = &device_creator<nes_ss88006_device>;
+const device_type NES_JF13 = &device_creator<nes_jf13_device>;
+const device_type NES_JF17_ADPCM = &device_creator<nes_jf17_adpcm_device>;
+const device_type NES_JF19_ADPCM = &device_creator<nes_jf19_adpcm_device>;
+const device_type NES_JF23 = &device_creator<nes_jf23_device>;
+const device_type NES_JF24 = &device_creator<nes_jf24_device>;
+const device_type NES_JF29 = &device_creator<nes_jf29_device>;
+const device_type NES_JF33 = &device_creator<nes_jf33_device>;
+
+
+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<cpu_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<samples_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<samples_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<samples_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<samples_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<samples_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<samples_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<samples_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<nes_jy_typea_device>;
+const device_type NES_JY_TYPEB = &device_creator<nes_jy_typeb_device>;
+const device_type NES_JY_TYPEC = &device_creator<nes_jy_typec_device>;
+
+
+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<cpu_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<nes_ks7058_device>;
+const device_type NES_KS7022 = &device_creator<nes_ks7022_device>;
+const device_type NES_KS7032 = &device_creator<nes_ks7032_device>;
+const device_type NES_KS202 = &device_creator<nes_ks202_device>;
+const device_type NES_KS7017 = &device_creator<nes_ks7017_device>;
+const device_type NES_KS7012 = &device_creator<nes_ks7012_device>;
+const device_type NES_KS7013B = &device_creator<nes_ks7013b_device>;
+const device_type NES_KS7031 = &device_creator<nes_ks7031_device>;
+
+
+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<cpu_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<cpu_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::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<kstudio_cart_interface *>(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::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::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<nes_kstudio_slot_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<nes_konami_vrc1_device>;
+const device_type NES_VRC2 = &device_creator<nes_konami_vrc2_device>;
+const device_type NES_VRC3 = &device_creator<nes_konami_vrc3_device>;
+const device_type NES_VRC4 = &device_creator<nes_konami_vrc4_device>;
+const device_type NES_VRC6 = &device_creator<nes_konami_vrc6_device>;
+const device_type NES_VRC7 = &device_creator<nes_konami_vrc7_device>;
+
+
+nes_konami_vrc1_device::nes_konami_vrc1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_VRC1, "NES Cart Konami VRC-1 PCB", tag, owner, clock, "nes_vrc1", __FILE__)
+{
+}
+
+nes_konami_vrc2_device::nes_konami_vrc2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_VRC2, "NES Cart Konami VRC-2 PCB", tag, owner, clock, "nes_vrc2", __FILE__)
+{
+}
+
+nes_konami_vrc3_device::nes_konami_vrc3_device(const machine_config &mconfig, 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<cpu_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<cpu_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<cpu_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<vrc6snd_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<ym2413_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<nes_ffe3_device>;
+const device_type NES_FFE4 = &device_creator<nes_ffe4_device>;
+const device_type NES_FFE8 = &device_creator<nes_ffe8_device>;
+
+
+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<cpu_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<nes_sxrom_device>;
+const device_type NES_SOROM = &device_creator<nes_sorom_device>;
+const device_type NES_SXROM_A = &device_creator<nes_sxrom_a_device>;
+const device_type NES_SOROM_A = &device_creator<nes_sorom_a_device>;
+
+
+
+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<nes_pxrom_device>;
+const device_type NES_FXROM = &device_creator<nes_fxrom_device>;
+
+
+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<nes_txrom_device>;
+const device_type NES_HKROM = &device_creator<nes_hkrom_device>;
+const device_type NES_TXSROM = &device_creator<nes_txsrom_device>;
+const device_type NES_TQROM = &device_creator<nes_tqrom_device>;
+const device_type NES_QJ_PCB = &device_creator<nes_qj_device>;
+const device_type NES_ZZ_PCB = &device_creator<nes_zz_device>;
+
+
+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<nes_nitra_device>;
+const device_type NES_KS7057 = &device_creator<nes_ks7057_device>;
+const device_type NES_SBROS11 = &device_creator<nes_sbros11_device>;
+const device_type NES_MALISB = &device_creator<nes_malisb_device>;
+const device_type NES_FAMILY4646 = &device_creator<nes_family4646_device>;
+const device_type NES_PIKAY2K = &device_creator<nes_pikay2k_device>;
+const device_type NES_8237 = &device_creator<nes_8237_device>;
+const device_type NES_SG_LIONK = &device_creator<nes_sglionk_device>;
+const device_type NES_SG_BOOG = &device_creator<nes_sgboog_device>;
+const device_type NES_KASING = &device_creator<nes_kasing_device>;
+const device_type NES_KAY = &device_creator<nes_kay_device>;
+const device_type NES_H2288 = &device_creator<nes_h2288_device>;
+const device_type NES_6035052 = &device_creator<nes_6035052_device>;
+const device_type NES_TXC_TW = &device_creator<nes_txc_tw_device>;
+const device_type NES_KOF97 = &device_creator<nes_kof97_device>;
+const device_type NES_KOF96 = &device_creator<nes_kof96_device>;
+const device_type NES_SF3 = &device_creator<nes_sf3_device>;
+const device_type NES_GOUDER = &device_creator<nes_gouder_device>;
+const device_type NES_SA9602B = &device_creator<nes_sa9602b_device>;
+const device_type NES_SACHEN_SHERO = &device_creator<nes_sachen_shero_device>;
+//const device_type NES_A9746 = &device_creator<nes_a9746_device>;
+
+const device_type NES_FK23C = &device_creator<nes_fk23c_device>;
+const device_type NES_FK23CA = &device_creator<nes_fk23ca_device>;
+const device_type NES_S24IN1SC03 = &device_creator<nes_s24in1sc03_device>;
+const device_type NES_BMC_15IN1 = &device_creator<nes_bmc_15in1_device>;
+const device_type NES_BMC_SBIG7 = &device_creator<nes_bmc_sbig7_device>;
+const device_type NES_BMC_HIK8 = &device_creator<nes_bmc_hik8_device>;
+const device_type NES_BMC_HIK4 = &device_creator<nes_bmc_hik4_device>;
+const device_type NES_BMC_MARIO7IN1 = &device_creator<nes_bmc_mario7in1_device>;
+const device_type NES_BMC_GOLD7IN1 = &device_creator<nes_bmc_gold7in1_device>;
+const device_type NES_BMC_GC6IN1 = &device_creator<nes_bmc_gc6in1_device>;
+const device_type NES_BMC_411120C = &device_creator<nes_bmc_411120c_device>;
+const device_type NES_BMC_830118C = &device_creator<nes_bmc_830118c_device>;
+const device_type NES_PJOY84 = &device_creator<nes_pjoy84_device>;
+
+
+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::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<ppu2c0x_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<ppu2c0x_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<ppu2c0x_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<nesapu_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<nes_action52_device>;
+const device_type NES_CALTRON6IN1 = &device_creator<nes_caltron_device>;
+const device_type NES_RUMBLESTATION = &device_creator<nes_rumblestat_device>;
+const device_type NES_SVISION16 = &device_creator<nes_svision16_device>;
+const device_type NES_N625092 = &device_creator<nes_n625092_device>;
+const device_type NES_A65AS = &device_creator<nes_a65as_device>;
+const device_type NES_T262 = &device_creator<nes_t262_device>;
+const device_type NES_NOVEL1 = &device_creator<nes_novel1_device>;
+const device_type NES_NOVEL2 = &device_creator<nes_novel2_device>;
+const device_type NES_STUDYNGAME = &device_creator<nes_studyngame_device>;
+const device_type NES_SUPERGUN20IN1 = &device_creator<nes_sgun20in1_device>;
+const device_type NES_VT5201 = &device_creator<nes_vt5201_device>;
+const device_type NES_810544C = &device_creator<nes_810544c_device>;
+const device_type NES_NTD03 = &device_creator<nes_ntd03_device>;
+const device_type NES_BMC_GB63 = &device_creator<nes_bmc_gb63_device>;
+const device_type NES_BMC_GKA = &device_creator<nes_bmc_gka_device>;
+const device_type NES_BMC_GKB = &device_creator<nes_bmc_gkb_device>;
+const device_type NES_BMC_WS = &device_creator<nes_bmc_ws_device>;
+const device_type NES_BMC_11160 = &device_creator<nes_bmc_11160_device>;
+const device_type NES_BMC_G146 = &device_creator<nes_bmc_g146_device>;
+const device_type NES_BMC_8157 = &device_creator<nes_bmc_8157_device>;
+const device_type NES_BMC_HIK300 = &device_creator<nes_bmc_hik300_device>;
+const device_type NES_BMC_S700 = &device_creator<nes_bmc_s700_device>;
+const device_type NES_BMC_BALL11 = &device_creator<nes_bmc_ball11_device>;
+const device_type NES_BMC_22GAMES = &device_creator<nes_bmc_22games_device>;
+const device_type NES_BMC_64Y2K = &device_creator<nes_bmc_64y2k_device>;
+const device_type NES_BMC_12IN1 = &device_creator<nes_bmc_12in1_device>;
+const device_type NES_BMC_20IN1 = &device_creator<nes_bmc_20in1_device>;
+const device_type NES_BMC_21IN1 = &device_creator<nes_bmc_21in1_device>;
+const device_type NES_BMC_31IN1 = &device_creator<nes_bmc_31in1_device>;
+const device_type NES_BMC_35IN1 = &device_creator<nes_bmc_35in1_device>;
+const device_type NES_BMC_36IN1 = &device_creator<nes_bmc_36in1_device>;
+const device_type NES_BMC_64IN1 = &device_creator<nes_bmc_64in1_device>;
+const device_type NES_BMC_70IN1 = &device_creator<nes_bmc_70in1_device>;
+const device_type NES_BMC_72IN1 = &device_creator<nes_bmc_72in1_device>;
+const device_type NES_BMC_76IN1 = &device_creator<nes_bmc_76in1_device>;
+const device_type NES_BMC_110IN1 = &device_creator<nes_bmc_110in1_device>;
+const device_type NES_BMC_150IN1 = &device_creator<nes_bmc_150in1_device>;
+const device_type NES_BMC_190IN1 = &device_creator<nes_bmc_190in1_device>;
+const device_type NES_BMC_800IN1 = &device_creator<nes_bmc_800in1_device>;
+const device_type NES_BMC_1200IN1 = &device_creator<nes_bmc_1200in1_device>;
+const device_type NES_BMC_GOLD150 = &device_creator<nes_bmc_gold150_device>;
+const device_type NES_BMC_GOLD260 = &device_creator<nes_bmc_gold260_device>;
+const device_type NES_BMC_CH001 = &device_creator<nes_bmc_ch001_device>;
+const device_type NES_BMC_SUPER22 = &device_creator<nes_bmc_super22_device>;
+const device_type NES_BMC_4IN1RESET = &device_creator<nes_bmc_4in1reset_device>;
+const device_type NES_BMC_42IN1RESET = &device_creator<nes_bmc_42in1reset_device>;
+
+
+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.
+
+ -----------