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.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bmc_70in1_device::write_h)
+{
+ LOG_MMC(("bmc70in1 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ if (offset < 0x4000)
+ {
+ set_nt_mirroring(BIT(offset, 5) ? PPU_MIRROR_HORZ: PPU_MIRROR_VERT);
+ chr8(offset & 7, CHRROM);
+ }
+ else
+ {
+ switch (offset & 0x30)
+ {
+ case 0x00: m_mode = 0x0; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x7); m_reg[1] = m_reg[0] | 0x7; break;
+ case 0x10: m_mode = 0x1; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x7); m_reg[1] = m_reg[0] | 0x7; break;
+ case 0x20: m_mode = 0x0; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x6); m_reg[1] = m_reg[0] | 0x1; break;
+ case 0x30: m_mode = 0x0; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x7); m_reg[1] = m_reg[0] | 0x0; break;
+ }
+ prg16_89ab(m_reg[0]);
+ prg16_cdef(m_reg[1]);
+ }
+}
+
+READ8_MEMBER(nes_bmc_70in1_device::read_h)
+{
+ LOG_MMC(("bmc70in1 read_h, offset: %04x\n", offset));
+
+ if (m_mode)
+ offset = (offset & 0x7ff0) | m_mode;
+
+ return hi_access_rom(offset);
+}
+
+/*-------------------------------------------------
+
+ BMC-72IN1
+
+ Unknown Bootleg Multigame Board
+ Games: 72 in 1, 115 in 1 and other multigame carts
+
+ iNES: mapper 225
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bmc_72in1_device::write_h)
+{
+ int hi_bank = offset & 0x40;
+ int size_16 = offset & 0x1000;
+ int bank = (offset & 0xf80) >> 7;
+
+ LOG_MMC(("bmc_72in1 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ chr8(offset, CHRROM);
+ set_nt_mirroring((offset & 0x2000) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ if (size_16)
+ {
+ bank <<= 1;
+ if (hi_bank)
+ bank ++;
+
+ prg16_89ab(bank);
+ prg16_cdef(bank);
+ }
+ else
+ prg32(bank);
+}
+
+/*-------------------------------------------------
+
+ BMC-76IN1
+
+ Unknown Bootleg Multigame Board
+ Games: 76 in 1, Super 42 in 1
+
+ iNES: mapper 226
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+// does this work for super42in1 as well?!?
+WRITE8_MEMBER(nes_bmc_76in1_device::write_h)
+{
+ int hi_bank;
+ int size_16;
+ int bank;
+
+ LOG_MMC(("bmc_76in1 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ if (offset & 0x01)
+ m_latch2 = data;
+ else
+ m_latch1 = data;
+
+ set_nt_mirroring(BIT(m_latch1, 6) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+
+ hi_bank = m_latch1 & 0x01;
+ size_16 = m_latch1 & 0x20;
+ bank = ((m_latch1 & 0x1e) >> 1) | ((m_latch1 & 0x80) >> 3) | ((m_latch2 & 0x01) << 5);
+
+ if (size_16)
+ {
+ bank <<= 1;
+ if (hi_bank)
+ bank ++;
+
+ prg16_89ab(bank);
+ prg16_cdef(bank);
+ }
+ else
+ prg32(bank);
+}
+
+/*-------------------------------------------------
+
+ BMC-110IN1
+
+ Known Boards: Unknown Bootleg Board
+ Games: 110 in 1
+
+ iNES: mapper 255
+
+ In MESS: Preliminary support.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bmc_110in1_device::write_h)
+{
+ UINT8 helper1 = (offset >> 12) ? 0 : 1;
+ UINT8 helper2 = ((offset >> 8) & 0x40) | ((offset >> 6) & 0x3f);
+
+ LOG_MMC(("bmc_110in1 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ set_nt_mirroring((offset & 0x2000) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ prg16_89ab(helper1 & ~helper2);
+ prg16_cdef(helper1 | helper2);
+ chr8(((offset >> 8) & 0x40) | (offset & 0x3f), CHRROM);
+}
+
+
+/*-------------------------------------------------
+
+ BMC-150IN1
+
+ Unknown Bootleg Multigame Board
+ Games: 150 in 1
+
+ iNES: mapper 202
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bmc_150in1_device::write_h)
+{
+ int bank = (offset >> 1) & 0x07;
+
+ LOG_MMC(("bmc_150in1 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ prg16_89ab(bank);
+ prg16_cdef(bank + (((bank & 0x06) == 0x06) ? 1 : 0));
+ chr8(bank, CHRROM);
+
+ set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ: PPU_MIRROR_VERT);
+}
+
+
+/*-------------------------------------------------
+
+ Board BMC-190IN1
+
+ Games: 190-in-1
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bmc_190in1_device::write_h)
+{
+ LOG_MMC(("bmc190in1 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ offset >>= 2;
+ prg16_89ab(offset);
+ prg16_cdef(offset);
+ chr8(offset, CHRROM);
+}
+
+/*-------------------------------------------------
+
+ BMC-800IN1
+
+ Unknown Bootleg Multigame Board
+ Games:
+
+ iNES: mapper
+
+ DSW not emulated yet.
+
+ In MESS: Preliminary Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bmc_800in1_device::write_h)
+{
+ LOG_MMC(("bmc800in1 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ if (offset < 0x4000)
+ {
+ set_nt_mirroring(BIT(offset, 5) ? PPU_MIRROR_HORZ: PPU_MIRROR_VERT);
+
+ m_reg[0] = (m_reg[0]) | ((offset << 3) & 0x38);
+ m_reg[1] = (m_reg[1]) | ((offset << 3) & 0x38);
+ }
+ else
+ {
+ switch (offset & 0x30)
+ {
+ case 0x00: m_mode = 0x0; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x7); m_reg[1] = m_reg[0] | 0x7; break;
+ case 0x10: m_mode = 0x1; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x7); m_reg[1] = m_reg[0] | 0x7; break;
+ case 0x20: m_mode = 0x0; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x6); m_reg[1] = m_reg[0] | 0x1; break;
+ case 0x30: m_mode = 0x0; m_reg[0] = (m_reg[0] & 0x38) | (offset & 0x7); m_reg[1] = m_reg[0] | 0x0; break;
+ }
+ }
+ prg16_89ab(m_reg[0]);
+ prg16_cdef(m_reg[1]);
+}
+
+READ8_MEMBER(nes_bmc_800in1_device::read_h)
+{
+ LOG_MMC(("bmc800in1 read_h, offset: %04x\n", offset));
+
+ if (m_mode)
+ offset = (offset & 0x7ff0) | m_mode;
+
+ return hi_access_rom(offset);
+}
+
+/*-------------------------------------------------
+
+ BMC-1200IN1
+
+ Unknown Bootleg Multigame Board
+ Games: 1200 in 1, 295 in 1, 76 in 1
+
+ iNES: mapper 227
+
+ In MESS: Preliminary Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bmc_1200in1_device::chr_w)
+{
+ int bank = offset >> 10;
+
+ if (!m_vram_protect)
+ m_chr_access[bank][offset & 0x3ff] = data;
+}
+
+
+WRITE8_MEMBER(nes_bmc_1200in1_device::write_h)
+{
+ int bank = ((offset >> 2) & 0x1f) | ((offset & 0x0100) >> 3);
+
+ LOG_MMC(("bmc_1200in1 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ if (offset & 0x80)
+ {
+// m_vram_protect = 1;
+ prg16_89ab(bank);
+ prg16_cdef(bank + (offset & 1));
+ }
+ else
+ {
+ int low_mask = (offset & 1) ? 0x3e : 0xff;
+
+// m_vram_protect = 0;
+ if (!BIT(offset, 9))
+ {
+ prg16_89ab(bank & low_mask);
+ prg16_cdef(bank & 0x38);
+ }
+ else
+ {
+ prg16_89ab(bank & low_mask);
+ prg16_cdef(bank | 0x07);
+ }
+ }
+
+ set_nt_mirroring(BIT(offset, 1) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+}
+
+
+/*-------------------------------------------------
+
+ BMC-GOLDEN260IN1
+
+ Unknown Bootleg Multigame Board
+ Games:
+
+ iNES: mapper 235
+
+ In MESS: Preliminary Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bmc_gold260_device::write_h)
+{
+ int bank = (offset & 0x1f) | ((offset & 0x0300) >> 3);
+ LOG_MMC(("bmc_gold260 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ if (offset & 0x400)
+ set_nt_mirroring(PPU_MIRROR_LOW);
+ else
+ set_nt_mirroring(BIT(offset, 13) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+
+ if (offset & 0x800)
+ {
+ bank = (bank << 1) | BIT(offset, 12);
+ prg16_89ab(bank);
+ prg16_cdef(bank);
+ }
+ else
+ prg32(bank);
+}
+
+
+/*-------------------------------------------------
+
+ BMC-GOLDEN150IN1
+
+ Unknown Bootleg Multigame Board
+ Games:
+
+ iNES: mapper 235
+
+ Same as the above + open bus in 0x8000-0xffff when
+ enabled
+
+ In MESS: Preliminary Supported.
+
+ -------------------------------------------------*/
+
+
+WRITE8_MEMBER(nes_bmc_gold150_device::write_h)
+{
+ int bank = (offset & 0x1f) | ((offset & 0x0200) >> 4);
+ LOG_MMC(("bmc_gold150 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ m_latch = (offset & 0x0100);
+
+ if (offset & 0x400)
+ set_nt_mirroring(PPU_MIRROR_LOW);
+ else
+ set_nt_mirroring(BIT(offset, 13) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+
+ if (offset & 0x800)
+ {
+ bank = (bank << 1) | BIT(offset, 12);
+ prg16_89ab(bank);
+ prg16_cdef(bank);
+ }
+ else
+ prg32(bank);
+}
+
+READ8_MEMBER(nes_bmc_gold150_device::read_h)
+{
+ LOG_MMC(("bmc_gold150 read_h, offset: %04x\n", offset));
+
+ if (m_latch) // open bus
+ return m_open_bus;
+ else
+ return hi_access_rom(offset);
+}
+
+
+/*-------------------------------------------------
+
+ BMC-POWERFUL-255
+
+ Unknown Bootleg Multigame Board
+ Games:
+
+ iNES: mapper 63
+
+
+ In MESS: Preliminary Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bmc_ch001_device::write_h)
+{
+ int bank = ((offset >> 1) & 0x1fc);
+ LOG_MMC(("bmc_ch001 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ m_latch = ((offset & 0x300) == 0x300);
+ set_nt_mirroring(BIT(offset, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+
+ if (offset & 2)
+ {
+ prg8_89(bank + 0);
+ prg8_ab(bank + 1);
+ prg8_cd(bank + 2);
+ prg8_ef(bank + 3);
+ }
+ else
+ {
+ bank |= (offset & 4) >> 1;
+ prg8_89(bank + 0);
+ prg8_ab(bank + 1);
+ prg8_cd(bank + 0);
+ prg8_ef(bank + 1);
+ }
+
+ if (offset & 0x800) // in this case, the last 8KB bank is switched differently...
+ prg8_ef((offset & 0x07c ) | ((offset & 0x6) ? 0x3 : 0x1));
+}
+
+READ8_MEMBER(nes_bmc_ch001_device::read_h)
+{
+ LOG_MMC(("bmc_ch001 read_h, offset: %04x\n", offset));
+
+ if (m_latch && offset < 0x4000) // open bus
+ return m_open_bus;
+ else
+ return hi_access_rom(offset);
+}
+
+/*-------------------------------------------------
+
+ BMC-SUPER22GAMES
+
+ Unknown Bootleg Multigame Board
+ Games:
+
+ iNES:
+
+ is there a dsw to access the higher banks above 0x20?
+
+ In MESS: Preliminary Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bmc_super22_device::write_h)
+{
+ LOG_MMC(("bmc_super22 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ if (data & 0x20)
+ {
+ prg16_89ab(data & 0x1f);
+ prg16_cdef(data & 0x1f);
+ }
+ else
+ prg32((data & 0x1f) >> 1);
+
+ switch (data & 0xc0)
+ {
+ case 0x00: set_nt_mirroring(PPU_MIRROR_LOW); break;
+ case 0x40: set_nt_mirroring(PPU_MIRROR_VERT); break;
+ case 0x80: set_nt_mirroring(PPU_MIRROR_HORZ); break;
+ case 0xc0: set_nt_mirroring(PPU_MIRROR_HIGH); break;
+ }
+}
+
+
+/*-------------------------------------------------
+
+ BMC-RESETBASED4IN1
+
+ Unknown Bootleg Multigame Board
+
+ Games:
+
+ iNES:
+
+ No need to use handlers. At reset the banks change
+ and so does the game.
+
+ In MESS: Preliminary Supported.
+
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ BMC-42IN1RESETBASED
+
+ Unknown Bootleg Multigame Board
+
+ Games:
+
+ iNES:
+
+ In MESS: Preliminary Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bmc_42in1reset_device::write_h)
+{
+ int bank;
+ LOG_MMC(("bmc_42in1reset write_h, offset: %04x, data: %02x\n", offset, data));
+
+ m_reg[offset & 1] = data;
+ bank = (m_reg[0] & 0x1f) | (m_latch << 5) | ((m_reg[1] & 1) << 6);
+
+ if (!(m_reg[0] & 0x20))
+ prg32(bank >> 1);
+ else
+ {
+ prg16_89ab(bank);
+ prg16_cdef(bank);
+ }
+
+ set_nt_mirroring(BIT(offset, 6) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+
+}
diff --git a/src/devices/bus/nes/multigame.h b/src/devices/bus/nes/multigame.h
new file mode 100644
index 00000000000..dbc824dfd54
--- /dev/null
+++ b/src/devices/bus/nes/multigame.h
@@ -0,0 +1,894 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_MULTIGAME_H
+#define __NES_MULTIGAME_H
+
+#include "nxrom.h"
+
+
+// ======================> nes_action52_device
+
+class nes_action52_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_action52_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_caltron_device
+
+class nes_caltron_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_caltron_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+};
+
+
+// ======================> nes_rumblestat_device
+
+class nes_rumblestat_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_rumblestat_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_prg, m_chr;
+};
+
+
+// ======================> nes_svision16_device
+
+class nes_svision16_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_svision16_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ void update_prg();
+ UINT8 m_latch1, m_latch2;
+};
+
+
+// ======================> nes_n625092_device
+
+class nes_n625092_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_n625092_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ void set_prg(UINT8 reg1, UINT8 reg2);
+ UINT8 m_latch1, m_latch2;
+};
+
+
+// ======================> nes_a65as_device
+
+class nes_a65as_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_a65as_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_t262_device
+
+class nes_t262_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_t262_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch1, m_latch2;
+};
+
+
+// ======================> nes_novel1_device
+
+class nes_novel1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_novel1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_novel2_device
+
+class nes_novel2_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_novel2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_studyngame_device
+
+class nes_studyngame_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_studyngame_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_sgun20in1_device
+
+class nes_sgun20in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sgun20in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_vt5201_device
+
+class nes_vt5201_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_vt5201_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_h);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch, m_dipsetting;
+};
+
+
+// ======================> nes_810544c_device
+
+class nes_810544c_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_810544c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_ntd03_device
+
+class nes_ntd03_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_ntd03_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_gb63_device
+
+class nes_bmc_gb63_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_gb63_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_h);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+ virtual DECLARE_WRITE8_MEMBER(chr_w);
+
+ virtual void pcb_reset();
+
+private:
+ void update_banks();
+ UINT8 m_latch, m_dipsetting;
+ UINT8 m_reg[2];
+ int m_vram_disable;
+};
+
+// ======================> nes_bmc_gka_device
+
+class nes_bmc_gka_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_gka_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch1, m_latch2;
+};
+
+
+// ======================> nes_bmc_gkb_device
+
+class nes_bmc_gkb_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_gkb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_ws_device
+
+class nes_bmc_ws_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_ws_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+};
+
+
+// ======================> nes_bmc_11160_device
+
+class nes_bmc_11160_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_11160_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_g146_device
+
+class nes_bmc_g146_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_g146_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_8157_device
+
+class nes_bmc_8157_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_8157_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_h);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_hik300_device
+
+class nes_bmc_hik300_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_hik300_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_s700_device
+
+class nes_bmc_s700_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_s700_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_ball11_device
+
+class nes_bmc_ball11_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_ball11_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ void set_banks();
+ UINT8 m_reg[2];
+};
+
+
+// ======================> nes_bmc_22games_device
+
+class nes_bmc_22games_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_22games_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_64y2k_device
+
+class nes_bmc_64y2k_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_64y2k_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ void set_prg();
+ UINT8 m_reg[4];
+};
+
+
+// ======================> nes_bmc_12in1_device
+
+class nes_bmc_12in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_12in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ void update_banks();
+ UINT8 m_reg[3];
+};
+
+
+// ======================> nes_bmc_20in1_device
+
+class nes_bmc_20in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_20in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_21in1_device
+
+class nes_bmc_21in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_21in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_31in1_device
+
+class nes_bmc_31in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_31in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_35in1_device
+
+class nes_bmc_35in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_35in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_36in1_device
+
+class nes_bmc_36in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_36in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_64in1_device
+
+class nes_bmc_64in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_64in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_70in1_device
+
+class nes_bmc_70in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_70in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_h);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_mode;
+ UINT8 m_reg[2];
+};
+
+
+// ======================> nes_bmc_72in1_device
+
+class nes_bmc_72in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_72in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_76in1_device
+
+class nes_bmc_76in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_76in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch1, m_latch2;
+};
+
+
+// ======================> nes_bmc_110in1_device
+
+class nes_bmc_110in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_110in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_150in1_device
+
+class nes_bmc_150in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_150in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_190in1_device
+
+class nes_bmc_190in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_190in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_800in1_device
+
+class nes_bmc_800in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_800in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_h);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_mode;
+ UINT8 m_reg[2];
+};
+
+
+// ======================> nes_bmc_1200in1_device
+
+class nes_bmc_1200in1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_1200in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+ virtual DECLARE_WRITE8_MEMBER(chr_w);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_vram_protect;
+};
+
+
+// ======================> nes_bmc_gold150_device
+
+class nes_bmc_gold150_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_gold150_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_h);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+};
+
+
+// ======================> nes_bmc_gold260_device
+
+class nes_bmc_gold260_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_gold260_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_ch001_device
+
+class nes_bmc_ch001_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_ch001_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_h);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+};
+
+
+// ======================> nes_bmc_super22_device
+
+class nes_bmc_super22_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_super22_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bmc_4in1reset_device
+
+class nes_bmc_4in1reset_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_4in1reset_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void pcb_reset();
+
+private:
+ int m_latch;
+};
+
+// ======================> nes_bmc_42in1reset_device
+
+class nes_bmc_42in1reset_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bmc_42in1reset_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ int m_latch;
+ UINT8 m_reg[2];
+};
+
+
+
+// device type definition
+extern const device_type NES_ACTION52;
+extern const device_type NES_CALTRON6IN1;
+extern const device_type NES_RUMBLESTATION;
+extern const device_type NES_SVISION16;
+extern const device_type NES_N625092;
+extern const device_type NES_A65AS;
+extern const device_type NES_T262;
+extern const device_type NES_NOVEL1;
+extern const device_type NES_NOVEL2;
+extern const device_type NES_STUDYNGAME;
+extern const device_type NES_SUPERGUN20IN1;
+extern const device_type NES_VT5201;
+extern const device_type NES_810544C;
+extern const device_type NES_NTD03;
+extern const device_type NES_BMC_GB63;
+extern const device_type NES_BMC_GKA;
+extern const device_type NES_BMC_GKB;
+extern const device_type NES_BMC_WS;
+extern const device_type NES_BMC_11160;
+extern const device_type NES_BMC_G146;
+extern const device_type NES_BMC_8157;
+extern const device_type NES_BMC_HIK300;
+extern const device_type NES_BMC_S700;
+extern const device_type NES_BMC_BALL11;
+extern const device_type NES_BMC_22GAMES;
+extern const device_type NES_BMC_64Y2K;
+extern const device_type NES_BMC_12IN1;
+extern const device_type NES_BMC_20IN1;
+extern const device_type NES_BMC_21IN1;
+extern const device_type NES_BMC_31IN1;
+extern const device_type NES_BMC_35IN1;
+extern const device_type NES_BMC_36IN1;
+extern const device_type NES_BMC_64IN1;
+extern const device_type NES_BMC_70IN1;
+extern const device_type NES_BMC_72IN1;
+extern const device_type NES_BMC_76IN1;
+extern const device_type NES_BMC_110IN1;
+extern const device_type NES_BMC_150IN1;
+extern const device_type NES_BMC_190IN1;
+extern const device_type NES_BMC_800IN1;
+extern const device_type NES_BMC_1200IN1;
+extern const device_type NES_BMC_GOLD150;
+extern const device_type NES_BMC_GOLD260;
+extern const device_type NES_BMC_CH001;
+extern const device_type NES_BMC_SUPER22;
+extern const device_type NES_BMC_4IN1RESET;
+extern const device_type NES_BMC_42IN1RESET;
+
+
+#endif
diff --git a/src/devices/bus/nes/namcot.c b/src/devices/bus/nes/namcot.c
new file mode 100644
index 00000000000..236effc4a24
--- /dev/null
+++ b/src/devices/bus/nes/namcot.c
@@ -0,0 +1,688 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for Namcot PCBs
+
+
+ Here we emulate the following PCBs
+
+ * Namcot 3433 & 3443 (aka DxROM) [mapper 88, 204, 154]
+ * Namcot 3446 [mapper 76]
+ * Namcot 3425 [mapper 95]
+ * Namcot 163 [mapper 19]
+ * Namcot 175 [mapper 210]
+ * Namcot 340 [mapper 210]
+
+ TODO:
+ - add sound feature of Namcot-163
+ - Quinty is not working (same issue of Mendel Palace on TxROM boards, of course)
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "namcot.h"
+#include "ui/ui.h"
+
+#include "cpu/m6502/m6502.h"
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_NAMCOT3433 = &device_creator<nes_namcot3433_device>;
+const device_type NES_NAMCOT3446 = &device_creator<nes_namcot3446_device>;
+const device_type NES_NAMCOT3425 = &device_creator<nes_namcot3425_device>;
+const device_type NES_NAMCOT175 = &device_creator<nes_namcot175_device>;
+const device_type NES_NAMCOT340 = &device_creator<nes_namcot340_device>;
+const device_type NES_NAMCOT163 = &device_creator<nes_namcot163_device>;
+
+
+nes_namcot3433_device::nes_namcot3433_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : nes_nrom_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+nes_namcot3433_device::nes_namcot3433_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_NAMCOT3433, "NES Cart Namcot 3433 & 3443 / DxROM PCB", tag, owner, clock, "nes_namcot3433", __FILE__)
+{
+}
+
+nes_namcot3446_device::nes_namcot3446_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_NAMCOT3446, "NES Cart Namcot 3446 PCB", tag, owner, clock, "nes_namcot3446", __FILE__)
+{
+}
+
+nes_namcot3425_device::nes_namcot3425_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_NAMCOT3425, "NES Cart Namcot 3425 PCB", tag, owner, clock, "nes_namcot3425", __FILE__)
+{
+}
+
+nes_namcot340_device::nes_namcot340_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : nes_nrom_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+nes_namcot340_device::nes_namcot340_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_NAMCOT175, "NES Cart Namcot 340 PCB", tag, owner, clock, "nes_namcot340", __FILE__)
+{
+}
+
+nes_namcot175_device::nes_namcot175_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_namcot340_device(mconfig, NES_NAMCOT340, "NES Cart Namcot 175 PCB", tag, owner, clock, "nes_namcot175", __FILE__)
+{
+}
+
+nes_namcot163_device::nes_namcot163_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_namcot340_device(mconfig, NES_NAMCOT163, "NES Cart Namcot 163 PCB", tag, owner, clock, "nes_namcot163", __FILE__)
+{
+}
+
+
+
+void nes_namcot3433_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+}
+
+void nes_namcot3433_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(m_prg_chunks - 2);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_latch = 0;
+}
+
+void nes_namcot3446_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+}
+
+void nes_namcot3446_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_latch = 0;
+}
+
+void nes_namcot3425_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+ save_item(NAME(m_reg));
+}
+
+void nes_namcot3425_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_latch = 0;
+ memset(m_reg, 0, sizeof(m_reg));
+}
+
+void nes_namcot340_device::device_start()
+{
+ common_start();
+ irq_timer = timer_alloc(TIMER_IRQ);
+ irq_timer->adjust(attotime::zero, 0, machine().device<cpu_device>("maincpu")->cycles_to_attotime(1));
+
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_n163_ram));
+
+ m_mapper_sram_size = 0x2000;
+ m_mapper_sram = m_n163_ram;
+}
+
+void nes_namcot340_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+ set_nt_mirroring(PPU_MIRROR_VERT);
+
+ m_irq_enable = 0;
+ m_irq_count = 0;
+}
+
+void nes_namcot175_device::device_start()
+{
+ common_start();
+ irq_timer = timer_alloc(TIMER_IRQ);
+ irq_timer->adjust(attotime::zero, 0, machine().device<cpu_device>("maincpu")->cycles_to_attotime(1));
+
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_wram_protect));
+ save_item(NAME(m_n163_ram));
+
+ m_mapper_sram_size = 0x2000;
+ m_mapper_sram = m_n163_ram;
+}
+
+void nes_namcot175_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+ set_nt_mirroring(PPU_MIRROR_VERT);
+
+ m_irq_enable = 0;
+ m_irq_count = 0;
+ m_wram_protect = 0;
+}
+
+void nes_namcot163_device::device_start()
+{
+ common_start();
+ irq_timer = timer_alloc(TIMER_IRQ);
+ irq_timer->adjust(attotime::zero, 0, machine().device<cpu_device>("maincpu")->cycles_to_attotime(1));
+
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_wram_protect));
+ save_item(NAME(m_latch));
+ save_item(NAME(m_chr_bank));
+ save_item(NAME(m_n163_ram));
+
+ m_mapper_sram_size = 0x2000;
+ m_mapper_sram = m_n163_ram;
+}
+
+void nes_namcot163_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+ set_nt_mirroring(PPU_MIRROR_VERT);
+
+ m_irq_enable = 0;
+ m_irq_count = 0;
+ m_wram_protect = 0;
+ m_latch = 0;
+ m_chr_bank = 0;
+}
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ DxROM & Namcot 3433 - 3443 - 3453 board emulation
+
+ Games: Dragon Spirit - Aratanaru Densetsu, Namcot Mahjong, Quinty,
+ Devilman
+
+ These are the same board, but DRROM (and Tengen 800004) have
+ 4-screen mirroring
+
+ iNES: mappers 88, 206 (same as 88 but possibly 4-screen mirroring),
+ and 154 (same as 88 but with additional mirroring control)
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_namcot3433_device::dxrom_write)
+{
+ LOG_MMC(("dxrom_write, offset: %04x, data: %02x\n", offset, data));
+
+ if (!(offset & 1) && m_pcb_ctrl_mirror)
+ set_nt_mirroring(BIT(data, 6) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW);
+
+ if (offset >= 0x2000)
+ return;
+
+ switch (offset & 1)
+ {
+ case 1:
+ switch (m_latch & 0x07)
+ {
+ case 0: chr2_0(data >> 1, CHRROM); break;
+ case 1: chr2_2(data >> 1, CHRROM); break;
+ case 2: chr1_4(data | 0x40, CHRROM); break;
+ case 3: chr1_5(data | 0x40, CHRROM); break;
+ case 4: chr1_6(data | 0x40, CHRROM); break;
+ case 5: chr1_7(data | 0x40, CHRROM); break;
+ case 6: prg8_89(data); break;
+ case 7: prg8_ab(data); break;
+ }
+ break;
+ case 0:
+ m_latch = data;
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+
+ Namcot 3446 board emulation
+
+ Games: Digital Devil Monogatari - Megami Tensei
+
+ These are similar Namcot 34x3, but different bankswitch capabilities
+
+ iNES: mapper 76
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_namcot3446_device::write_h)
+{
+ LOG_MMC(("namcot3446 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ // NEStopia does not have this!
+ if (offset >= 0x2000)
+ {
+ if (!(offset & 1))
+ set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ return;
+ }
+
+ switch (offset & 1)
+ {
+ case 1:
+ switch (m_latch & 0x07)
+ {
+ case 2: chr2_0(data, CHRROM); break;
+ case 3: chr2_2(data, CHRROM); break;
+ case 4: chr2_4(data, CHRROM); break;
+ case 5: chr2_6(data, CHRROM); break;
+ case 6: BIT(m_latch, 6) ? prg8_cd(data) : prg8_89(data); break;
+ case 7: prg8_ab(data); break;
+ }
+ break;
+ case 0:
+ m_latch = data;
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Namcot 3425 board emulation
+
+ Games: Dragon Buster
+
+ These are similar Namcot 34x3, but with NT mirroring (two
+ different modes)
+
+ iNES: mapper 95
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_namcot3425_device::write_h)
+{
+ UINT8 mode;
+ LOG_MMC(("namcot3425 write_h, offset: %04x, data: %02x\n", offset, data));
+ if (offset >= 0x2000)
+ return;
+
+ switch (offset & 1)
+ {
+ case 1:
+ mode = m_latch & 0x07;
+ switch (mode)
+ {
+ case 0: chr2_0(data >> 1, CHRROM); break;
+ case 1: chr2_2(data >> 1, CHRROM); break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ chr1_x(2 + mode, data, CHRROM);
+ m_reg[mode - 2] = BIT(data, 5);
+ if (!BIT(m_latch, 7))
+ {
+ set_nt_page(0, CIRAM, m_reg[0], 1);
+ set_nt_page(1, CIRAM, m_reg[1], 1);
+ set_nt_page(2, CIRAM, m_reg[2], 1);
+ set_nt_page(3, CIRAM, m_reg[3], 1);
+ }
+ else
+ set_nt_mirroring(PPU_MIRROR_HORZ);
+ break;
+ case 6: prg8_89(data); break;
+ case 7: prg8_ab(data); break;
+ }
+ break;
+ case 0:
+ m_latch = data;
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Namcot-340 board emulation
+
+ Games: Famista '92, '93 & '94, Top Striker,
+ Wagyan Land 2 & 3
+
+ This (and Namcot-175 below) is a cut-down version
+ of the Namcot-163 chip, without the sound capabilities.
+ They also cannot use NTRAM as VRAM and differ for
+ the mirroring handling
+
+ iNES: mapper 210
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+void nes_namcot340_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if (id == TIMER_IRQ)
+ {
+ if (m_irq_enable)
+ {
+ if (m_irq_count == 0x7fff) // counter does not wrap to 0!
+ m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
+ else
+ m_irq_count++;
+ }
+ }
+}
+
+WRITE8_MEMBER(nes_namcot340_device::n340_lowrite)
+{
+ LOG_MMC(("n340_lowrite, offset: %04x, data: %02x\n", offset, data));
+ offset += 0x100;
+
+ switch (offset & 0x1800)
+ {
+ case 0x1000: /* low byte of IRQ */
+ m_irq_count = (m_irq_count & 0x7f00) | data;
+ m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
+ break;
+ case 0x1800: /* high byte of IRQ, IRQ enable in high bit */
+ m_irq_count = (m_irq_count & 0xff) | ((data & 0x7f) << 8);
+ m_irq_enable = data & 0x80;
+ m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
+ break;
+ }
+}
+
+READ8_MEMBER(nes_namcot340_device::n340_loread)
+{
+ LOG_MMC(("n340_loread, offset: %04x\n", offset));
+ offset += 0x100;
+
+ switch (offset & 0x1800)
+ {
+ case 0x1000:
+ return m_irq_count & 0xff;
+ m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
+ case 0x1800:
+ return (m_irq_count >> 8) & 0xff;
+ m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
+ default:
+ return 0x00;
+ }
+}
+
+WRITE8_MEMBER(nes_namcot340_device::n340_hiwrite)
+{
+ LOG_MMC(("n340_hiwrite, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x7800)
+ {
+ case 0x0000: case 0x0800:
+ case 0x1000: case 0x1800:
+ case 0x2000: case 0x2800:
+ case 0x3000: case 0x3800:
+ chr1_x(offset / 0x800, data, CHRROM);
+ break;
+ case 0x4000:
+ // no cart found with wram, so it is not clear if this could work as in Namcot-175...
+ break;
+ case 0x6000:
+ switch (data & 0xc0)
+ {
+ case 0x00:
+ set_nt_mirroring(PPU_MIRROR_LOW);
+ break;
+ case 0x40:
+ set_nt_mirroring(PPU_MIRROR_VERT);
+ break;
+ case 0x80:
+ set_nt_mirroring(PPU_MIRROR_HIGH);
+ break;
+ case 0xc0:
+ set_nt_mirroring(PPU_MIRROR_HORZ);
+ break;
+ }
+ prg8_89(data & 0x3f);
+ break;
+ case 0x6800:
+ prg8_ab(data & 0x3f);
+ break;
+ case 0x7000:
+ prg8_cd(data & 0x3f);
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+
+ Namcot-175 board emulation
+
+ Games: Chibi Maruko-chan, Family Circuit '91,
+ Famista '91
+
+ This (and Namcot-340 above) is a cut-down version
+ of the Namcot-163 chip, without the sound capabilities.
+ They also cannot use NTRAM as VRAM and differ for
+ the mirroring handling
+
+ iNES: mapper 210
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+READ8_MEMBER(nes_namcot175_device::read_m)
+{
+ // the only game supporting this is Family Circuit '91, and it has 2KB of battery
+ // but it's mirrored up to 8KB (see Sprint Race -> Back Up menu breakage if not)
+ if (!m_battery.empty() && !m_wram_protect)
+ return m_battery[offset & (m_battery.size() - 1)];
+
+ return m_open_bus; // open bus
+}
+
+WRITE8_MEMBER(nes_namcot175_device::write_m)
+{
+ // the only game supporting this is Family Circuit '91, and it has 2KB of battery
+ // but it's mirrored up to 8KB (see Sprint Race -> Back Up menu breakage if not)
+ if (!m_battery.empty() && !m_wram_protect)
+ m_battery[offset & (m_battery.size() - 1)] = data;
+}
+
+WRITE8_MEMBER(nes_namcot175_device::write_h)
+{
+ LOG_MMC(("namcot175 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x7800)
+ {
+ case 0x4000:
+ m_wram_protect = data & 1;
+ break;
+ case 0x6000:
+ prg8_89(data & 0x3f);
+ break;
+ default:
+ n340_hiwrite(space, offset, data, mem_mask);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Namcot-163 board emulation
+
+ Games: Battle Fleet, Famista '90, Megami Tensei II,
+ Juvei Quest, etc.
+
+ Compared to Namcot-175 here we have mapper controlled
+ mirroring, NTRAM mapping to VRAM and additional
+ sound hw inside the chip (currently unemulated) and
+ some internal RAM.
+
+ iNES: mapper 19
+
+ In MESS: Supported (with no emulation of the
+ sound component)
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_namcot163_device::chr_w)
+{
+ int bank = offset >> 10;
+
+ if (!(m_latch & 0x40) && m_chr_bank >= 0xe0)
+ {
+ // CIRAM!!!
+ machine().ui().popup_time(10, "CIRAM mapped to VRAM. Please contact MAMEDevs.");
+
+ if (!m_nt_writable[bank & 0x03])
+ return;
+ m_nt_access[bank & 0x03][offset & 0x3ff] = data;
+ }
+ // or ROM, so no write
+}
+
+READ8_MEMBER(nes_namcot163_device::chr_r)
+{
+ int bank = offset >> 10;
+ if (!(m_latch & 0x40) && m_chr_bank >= 0xe0)
+ {
+ // CIRAM!!!
+ machine().ui().popup_time(10, "CIRAM mapped to VRAM. Please contact MAMEDevs.");
+ return m_nt_access[bank & 0x03][offset & 0x3ff];
+ }
+ // or ROM, accessed as usual
+ return m_chr_access[bank][offset & 0x3ff];
+}
+
+
+READ8_MEMBER(nes_namcot163_device::read_m)
+{
+ if (!m_battery.empty() && offset < m_battery.size())
+ return m_battery[offset & (m_battery.size() - 1)];
+
+ return m_open_bus; // open bus
+}
+
+WRITE8_MEMBER(nes_namcot163_device::write_m)
+{
+ // the pcb can separately protect each 2KB chunk of the external wram from writes
+ int bank = (offset & 0x1800) >> 11;
+ if (!m_battery.empty() && !BIT(m_wram_protect, bank))
+ m_battery[offset & (m_battery.size() - 1)] = data;
+}
+
+WRITE8_MEMBER(nes_namcot163_device::write_l)
+{
+ LOG_MMC(("namcot163 write_l, offset: %04x, data: %02x\n", offset, data));
+ offset += 0x100;
+
+ switch (offset & 0x1800)
+ {
+ case 0x0800:
+ LOG_MMC(("Namcot-163 sound reg write, data: %02x\n", data));
+ break;
+ default:
+ n340_lowrite(space, offset, data, mem_mask);
+ break;
+ }
+}
+
+READ8_MEMBER(nes_namcot163_device::read_l)
+{
+ LOG_MMC(("namcot163 read_l, offset: %04x\n", offset));
+ offset += 0x100;
+
+ switch (offset & 0x1800)
+ {
+ case 0x0800:
+ LOG_MMC(("Namcot-163 sound reg read\n"));
+ return 0;
+ default:
+ return n340_loread(space, offset, mem_mask);
+ }
+}
+
+void nes_namcot163_device::set_mirror(UINT8 page, UINT8 data)
+{
+ if (data < 0xe0)
+ set_nt_page(page, VROM, data, 0);
+ else
+ set_nt_page(page, CIRAM, data & 0x01, 1);
+}
+
+WRITE8_MEMBER(nes_namcot163_device::write_h)
+{
+ int page;
+ LOG_MMC(("namcot163 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x7800)
+ {
+ case 0x0000: case 0x0800:
+ case 0x1000: case 0x1800:
+ case 0x2000: case 0x2800:
+ case 0x3000: case 0x3800:
+ m_chr_bank = data;
+ chr1_x(offset / 0x800, m_chr_bank, CHRROM);
+ break;
+ case 0x4000:
+ case 0x4800:
+ case 0x5000:
+ case 0x5800:
+ page = (offset & 0x1800) >> 11;
+ set_mirror(page, data);
+ break;
+ case 0x6000:
+ // TODO: data & 40 (or data & c0) disable sound if set
+ prg8_89(data & 0x3f);
+ break;
+ case 0x6800:
+ m_latch = data & 0xc0;
+ prg8_ab(data & 0x3f);
+ break;
+ case 0x7800:
+ // the lower 4 bits work *BOTH* as WRAM write protect *AND* as sound address!
+ m_wram_protect = data & 0x0f;
+ LOG_MMC(("Namcot-163 sound address write, data: %02x\n", data));
+ break;
+ default:
+ n340_hiwrite(space, offset, data, mem_mask);
+ break;
+ }
+}
diff --git a/src/devices/bus/nes/namcot.h b/src/devices/bus/nes/namcot.h
new file mode 100644
index 00000000000..268347068b9
--- /dev/null
+++ b/src/devices/bus/nes/namcot.h
@@ -0,0 +1,163 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_NAMCOT_H
+#define __NES_NAMCOT_H
+
+#include "nxrom.h"
+
+
+// ======================> nes_namcot3433_device
+
+class nes_namcot3433_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_namcot3433_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_namcot3433_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(dxrom_write);
+ virtual DECLARE_WRITE8_MEMBER(write_h) { dxrom_write(space, offset, data, mem_mask); }
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+};
+
+
+// ======================> nes_namcot3446_device
+
+class nes_namcot3446_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_namcot3446_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+};
+
+
+// ======================> nes_namcot3425_device
+
+class nes_namcot3425_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_namcot3425_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+ UINT8 m_reg[4];
+};
+
+
+// ======================> nes_namcot340_device
+
+class nes_namcot340_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_namcot340_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_namcot340_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual DECLARE_READ8_MEMBER(n340_loread);
+ virtual DECLARE_WRITE8_MEMBER(n340_lowrite);
+ virtual DECLARE_WRITE8_MEMBER(n340_hiwrite);
+ virtual DECLARE_READ8_MEMBER(read_l) { return n340_loread(space, offset, mem_mask); }
+ virtual DECLARE_WRITE8_MEMBER(write_l) { n340_lowrite(space, offset, data, mem_mask); }
+ virtual DECLARE_WRITE8_MEMBER(write_h) { n340_hiwrite(space, offset, data, mem_mask); }
+
+ virtual void pcb_reset();
+
+protected:
+ UINT16 m_irq_count;
+ int m_irq_enable;
+
+ static const device_timer_id TIMER_IRQ = 0;
+ emu_timer *irq_timer;
+
+ // Namcot-163 chip contains 8K of internal ram, possibly battery backed up (not emulated yet)
+ // was it also present in 175 & 340 chips?
+ UINT8 m_n163_ram[0x2000];
+};
+
+
+// ======================> nes_namcot175_device
+
+class nes_namcot175_device : public nes_namcot340_device
+{
+public:
+ // construction/destruction
+ nes_namcot175_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_wram_protect;
+};
+
+
+// ======================> nes_namcot163_device
+
+class nes_namcot163_device : public nes_namcot340_device
+{
+public:
+ // construction/destruction
+ nes_namcot163_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_l);
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ // we have to overwrite these to allow CIRAM to be used for VRAM, even if it's not clear which game(s) use this
+ virtual DECLARE_READ8_MEMBER(chr_r);
+ virtual DECLARE_WRITE8_MEMBER(chr_w);
+
+ virtual void pcb_reset();
+
+private:
+ void set_mirror(UINT8 page, UINT8 data);
+
+ UINT8 m_wram_protect, m_latch, m_chr_bank;
+ // TODO: add emulation of the sound part of the chip
+};
+
+
+
+// device type definition
+extern const device_type NES_NAMCOT3433;
+extern const device_type NES_NAMCOT3446;
+extern const device_type NES_NAMCOT3425;
+extern const device_type NES_NAMCOT175;
+extern const device_type NES_NAMCOT340;
+extern const device_type NES_NAMCOT163;
+
+#endif
diff --git a/src/devices/bus/nes/nanjing.c b/src/devices/bus/nes/nanjing.c
new file mode 100644
index 00000000000..4d0fc022520
--- /dev/null
+++ b/src/devices/bus/nes/nanjing.c
@@ -0,0 +1,175 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for Nanjing PCBs
+
+ TODO:
+ - Emulate the variants often assigned to mapper 162/164 (and investigate connection with Waixing FS-304)!
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "nanjing.h"
+
+#include "video/ppu2c0x.h"
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_NANJING = &device_creator<nes_nanjing_device>;
+
+
+nes_nanjing_device::nes_nanjing_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_NANJING, "NES Cart Nanjing PCB", tag, owner, clock, "nes_nanjing", __FILE__)
+{
+}
+
+
+
+
+void nes_nanjing_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_count));
+ save_item(NAME(m_latch1));
+ save_item(NAME(m_latch2));
+ save_item(NAME(m_reg));
+}
+
+void nes_nanjing_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(m_prg_chunks - 2);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_count = 0xff;
+ m_latch1 = 0;
+ m_latch2 = 0;
+ m_reg[0] = 0xff;
+ m_reg[1] = 0;
+}
+
+
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ Bootleg Board by Nanjing
+
+ Games: A lot of pirate originals
+
+ iNES: mapper 163
+
+ In MESS: Unsupported.
+
+ -------------------------------------------------*/
+
+void nes_nanjing_device::hblank_irq(int scanline, int vblank, int blanked)
+{
+ if (BIT(m_reg[0], 7))
+ {
+ if (scanline == 127)
+ {
+ chr4_0(1, CHRRAM);
+ chr4_4(1, CHRRAM);
+ }
+
+ if (scanline == 239)
+ {
+ chr4_0(0, CHRRAM);
+ chr4_4(0, CHRRAM);
+ }
+ }
+
+}
+
+WRITE8_MEMBER(nes_nanjing_device::write_l)
+{
+ LOG_MMC(("nanjing write_l, offset: %04x, data: %02x\n", offset, data));
+
+ offset += 0x100;
+
+ if (offset < 0x1000)
+ return;
+
+ if (offset == 0x1100) // 0x5100
+ {
+ if (data == 6)
+ prg32(3);
+ return;
+ }
+
+ if (offset == 0x1101) // 0x5101
+ {
+ UINT8 temp = m_count;
+ m_count = data;
+
+ if (temp & !data)
+ m_latch2 ^= 0xff;
+ }
+
+ switch (offset & 0x300)
+ {
+ case 0x000:
+ case 0x200:
+ m_reg[BIT(offset, 9)] = data;
+ if (!BIT(m_reg[0], 7) && machine().device<ppu2c0x_device>("ppu")->get_current_scanline() <= 127)
+ chr8(0, CHRRAM);
+ break;
+ case 0x300:
+ m_latch1 = data;
+ break;
+ }
+
+ prg32((m_reg[0] & 0x0f) | ((m_reg[1] & 0x0f) << 4));
+}
+
+READ8_MEMBER(nes_nanjing_device::read_l)
+{
+ UINT8 value = 0;
+ LOG_MMC(("nanjing read_l, offset: %04x\n", offset));
+
+ offset += 0x100;
+
+ if (offset < 0x1000)
+ return 0;
+
+ switch (offset & 0x700)
+ {
+ case 0x100:
+ value = m_latch1;
+ break;
+ case 0x500:
+ value = m_latch2 & m_latch1;
+ break;
+ case 0x000:
+ case 0x200:
+ case 0x300:
+ case 0x400:
+ case 0x600:
+ case 0x700:
+ value = 4;
+ break;
+ }
+ return value;
+}
diff --git a/src/devices/bus/nes/nanjing.h b/src/devices/bus/nes/nanjing.h
new file mode 100644
index 00000000000..40fb8c2e714
--- /dev/null
+++ b/src/devices/bus/nes/nanjing.h
@@ -0,0 +1,38 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_NANJING_H
+#define __NES_NANJING_H
+
+#include "nxrom.h"
+
+
+// ======================> nes_nanjing_device
+
+class nes_nanjing_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_nanjing_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_l);
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+
+ virtual void hblank_irq(int scanline, int vblank, int blanked);
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_count;
+ UINT8 m_reg[2];
+ UINT8 m_latch1, m_latch2;
+};
+
+
+
+
+
+// device type definition
+extern const device_type NES_NANJING;
+
+#endif
diff --git a/src/devices/bus/nes/nes_carts.c b/src/devices/bus/nes/nes_carts.c
new file mode 100644
index 00000000000..6acac2fc6e7
--- /dev/null
+++ b/src/devices/bus/nes/nes_carts.c
@@ -0,0 +1,366 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/**********************************************************************
+
+ NES carts
+
+**********************************************************************/
+
+#include "nes_carts.h"
+
+SLOT_INTERFACE_START(nes_cart)
+// HROM, NROM, RROM, SROM & STROM
+ SLOT_INTERFACE_INTERNAL("nrom", NES_NROM)
+// Nintendo Family BASIC pcb (NROM + 2K or 4K WRAM)
+ SLOT_INTERFACE_INTERNAL("hvc_basic", NES_FCBASIC)
+// Extended NROM-368 board (NROM with 46K PRG)
+ SLOT_INTERFACE_INTERNAL("nrom368", NES_NROM368)
+// Game Genie
+ SLOT_INTERFACE_INTERNAL("nrom_gg", NES_GGENIE)
+// UNROM/UOROM
+ SLOT_INTERFACE_INTERNAL("uxrom", NES_UXROM)
+ SLOT_INTERFACE_INTERNAL("unrom_cc", NES_UXROM_CC)
+// CNROM
+ SLOT_INTERFACE_INTERNAL("cnrom", NES_CNROM)
+// Bandai PT-554 (CNROM boards + special audio chip, used by Aerobics Studio)
+ SLOT_INTERFACE_INTERNAL("bandai_pt554", NES_BANDAI_PT554)
+// CPROM
+ SLOT_INTERFACE_INTERNAL("cprom", NES_CPROM)
+// AMROM, ANROM, AOROM
+ SLOT_INTERFACE_INTERNAL("axrom", NES_AXROM)
+// PxROM
+ SLOT_INTERFACE_INTERNAL("pxrom", NES_PXROM)
+// FxROM
+ SLOT_INTERFACE_INTERNAL("fxrom", NES_FXROM)
+// BNROM
+ SLOT_INTERFACE_INTERNAL("bnrom", NES_BXROM)
+// GNROM & MHROM
+ SLOT_INTERFACE_INTERNAL("gxrom", NES_GXROM)
+// UN1ROM
+ SLOT_INTERFACE_INTERNAL("un1rom", NES_UN1ROM)
+// SxROM
+ SLOT_INTERFACE_INTERNAL("sxrom", NES_SXROM)
+ SLOT_INTERFACE_INTERNAL("sorom", NES_SOROM)
+ SLOT_INTERFACE_INTERNAL("sxrom_a", NES_SXROM_A) // in MMC1-A PRG RAM is always enabled
+ SLOT_INTERFACE_INTERNAL("sorom_a", NES_SOROM_A) // in MMC1-A PRG RAM is always enabled
+// TxROM
+ SLOT_INTERFACE_INTERNAL("txrom", NES_TXROM)
+// HKROM
+ SLOT_INTERFACE_INTERNAL("hkrom", NES_HKROM)
+// TQROM
+ SLOT_INTERFACE_INTERNAL("tqrom", NES_TQROM)
+// TxSROM
+ SLOT_INTERFACE_INTERNAL("txsrom", NES_TXSROM)
+// ExROM
+ SLOT_INTERFACE_INTERNAL("exrom", NES_EXROM)
+// RAM expansion + Disk System add-on
+ SLOT_INTERFACE_INTERNAL("disksys", NES_DISKSYS)
+// Nintendo Custom boards
+ SLOT_INTERFACE_INTERNAL("pal_zz", NES_ZZ_PCB)
+ SLOT_INTERFACE_INTERNAL("nes_qj", NES_QJ_PCB)
+ SLOT_INTERFACE_INTERNAL("nes_event", NES_EVENT)
+// Discrete Components boards
+// IC_74x139x74
+ SLOT_INTERFACE_INTERNAL("discrete_74x139", NES_74X139X74)
+// IC_74x377
+ SLOT_INTERFACE_INTERNAL("discrete_74x377", NES_74X377)
+// Discrete board IC_74x161x161x32
+ SLOT_INTERFACE_INTERNAL("discrete_74x161", NES_74X161X161X32)
+// Discrete board IC_74x161x138
+ SLOT_INTERFACE_INTERNAL("bitcorp_dis", NES_74X161X138)
+// Bandai boards
+ SLOT_INTERFACE_INTERNAL("lz93d50", NES_LZ93D50)
+ SLOT_INTERFACE_INTERNAL("lz93d50_ep1", NES_LZ93D50_24C01)
+ SLOT_INTERFACE_INTERNAL("lz93d50_ep2", NES_LZ93D50_24C02)
+ SLOT_INTERFACE_INTERNAL("fcg", NES_FCG)
+ SLOT_INTERFACE_INTERNAL("fjump2", NES_FJUMP2)
+ SLOT_INTERFACE_INTERNAL("datach", NES_DATACH)
+ SLOT_INTERFACE_INTERNAL("karastudio", NES_KARAOKESTUDIO)
+ SLOT_INTERFACE_INTERNAL("oekakids", NES_OEKAKIDS)
+// Irem boards
+ SLOT_INTERFACE_INTERNAL("g101", NES_G101)
+ SLOT_INTERFACE_INTERNAL("lrog017", NES_LROG017)
+ SLOT_INTERFACE_INTERNAL("h3001", NES_H3001)
+ SLOT_INTERFACE_INTERNAL("holydivr", NES_HOLYDIVR)
+ SLOT_INTERFACE_INTERNAL("tam_s1", NES_TAM_S1)
+// Jaleco boards
+ SLOT_INTERFACE_INTERNAL("jf11", NES_JF11)
+ SLOT_INTERFACE_INTERNAL("jf13", NES_JF13)
+ SLOT_INTERFACE_INTERNAL("jf16", NES_JF16)
+ SLOT_INTERFACE_INTERNAL("jf17", NES_JF17)
+ SLOT_INTERFACE_INTERNAL("jf17pcm", NES_JF17_ADPCM)
+ SLOT_INTERFACE_INTERNAL("jf19", NES_JF19)
+ SLOT_INTERFACE_INTERNAL("jf19pcm", NES_JF19_ADPCM)
+ SLOT_INTERFACE_INTERNAL("ss88006", NES_SS88006)
+ SLOT_INTERFACE_INTERNAL("jf23", NES_JF23)
+ SLOT_INTERFACE_INTERNAL("jf24", NES_JF24)
+ SLOT_INTERFACE_INTERNAL("jf29", NES_JF29)
+ SLOT_INTERFACE_INTERNAL("jf33", NES_JF33)
+// Konami boards
+ SLOT_INTERFACE_INTERNAL("vrc1", NES_VRC1)
+ SLOT_INTERFACE_INTERNAL("vrc2", NES_VRC2)
+ SLOT_INTERFACE_INTERNAL("vrc3", NES_VRC3)
+ SLOT_INTERFACE_INTERNAL("vrc4", NES_VRC4)
+ SLOT_INTERFACE_INTERNAL("vrc6", NES_VRC6)
+ SLOT_INTERFACE_INTERNAL("vrc7", NES_VRC7)
+// Namcot boards
+ SLOT_INTERFACE_INTERNAL("namcot_163", NES_NAMCOT163)
+ SLOT_INTERFACE_INTERNAL("namcot_175", NES_NAMCOT175)
+ SLOT_INTERFACE_INTERNAL("namcot_340", NES_NAMCOT340)
+ SLOT_INTERFACE_INTERNAL("namcot_3433", NES_NAMCOT3433) // DxROM is a Nintendo board for US versions of the 3433/3443 games
+ SLOT_INTERFACE_INTERNAL("namcot_3425", NES_NAMCOT3425)
+ SLOT_INTERFACE_INTERNAL("namcot_3446", NES_NAMCOT3446)
+// Sunsoft boards
+ SLOT_INTERFACE_INTERNAL("sunsoft1", NES_SUNSOFT_1)
+ SLOT_INTERFACE_INTERNAL("sunsoft2", NES_SUNSOFT_2)
+ SLOT_INTERFACE_INTERNAL("sunsoft3", NES_SUNSOFT_3)
+ SLOT_INTERFACE_INTERNAL("sunsoft4", NES_SUNSOFT_4)
+ SLOT_INTERFACE_INTERNAL("sunsoft_dcs", NES_SUNSOFT_DCS)
+ SLOT_INTERFACE_INTERNAL("sunsoft_fme7", NES_SUNSOFT_FME7) // JxROM is a Nintendo board for US versions of the Sunsoft FME7 games
+ SLOT_INTERFACE_INTERNAL("sunsoft5a", NES_SUNSOFT_5)
+ SLOT_INTERFACE_INTERNAL("sunsoft5b", NES_SUNSOFT_5)
+// Taito boards
+ SLOT_INTERFACE_INTERNAL("tc0190fmc", NES_TC0190FMC)
+ SLOT_INTERFACE_INTERNAL("tc0190fmcp", NES_TC0190FMC_PAL16R4)
+ SLOT_INTERFACE_INTERNAL("tc0350fmr", NES_TC0190FMC)
+ SLOT_INTERFACE_INTERNAL("x1_005", NES_X1_005) // two variants exist, depending on pin17 & pin31 connections
+ SLOT_INTERFACE_INTERNAL("x1_017", NES_X1_017)
+// Misc pirate boards (by AVE, Camerica, C&E, Nanjing, NTDEC, JY Company, Sachen, Tengen, TXC, Waixing, Henggendianzi, etc.)
+ SLOT_INTERFACE_INTERNAL("nina001", NES_NINA001)
+ SLOT_INTERFACE_INTERNAL("nina006", NES_NINA006)
+ SLOT_INTERFACE_INTERNAL("bf9093", NES_BF9093)
+ SLOT_INTERFACE_INTERNAL("bf9096", NES_BF9096)
+ SLOT_INTERFACE_INTERNAL("goldenfive", NES_GOLDEN5)
+ SLOT_INTERFACE_INTERNAL("ade", NES_ALADDIN)
+ SLOT_INTERFACE_INTERNAL("cne_decathl", NES_CNE_DECATHL)
+ SLOT_INTERFACE_INTERNAL("cne_fsb", NES_CNE_FSB)
+ SLOT_INTERFACE_INTERNAL("cne_shlz", NES_CNE_SHLZ)
+ SLOT_INTERFACE_INTERNAL("nanjing", NES_NANJING) // mapper 163
+ SLOT_INTERFACE_INTERNAL("ntdec_asder", NES_NTDEC_ASDER) // mapper 112
+ SLOT_INTERFACE_INTERNAL("ntdec_fh", NES_NTDEC_FH) // mapper 193
+ SLOT_INTERFACE_INTERNAL("jyc_a", NES_JY_TYPEA) // mapper 90
+ SLOT_INTERFACE_INTERNAL("jyc_b", NES_JY_TYPEB) // mapper 211
+ SLOT_INTERFACE_INTERNAL("jyc_c", NES_JY_TYPEC) // mapper 209
+ SLOT_INTERFACE_INTERNAL("sa009", NES_SACHEN_SA009)
+ SLOT_INTERFACE_INTERNAL("sa0036", NES_SACHEN_SA0036)
+ SLOT_INTERFACE_INTERNAL("sa0037", NES_SACHEN_SA0037)
+ SLOT_INTERFACE_INTERNAL("sa72007", NES_SACHEN_SA72007)
+ SLOT_INTERFACE_INTERNAL("sa72008", NES_SACHEN_SA72008)
+ SLOT_INTERFACE_INTERNAL("tca01", NES_SACHEN_TCA01)
+ SLOT_INTERFACE_INTERNAL("s8259a", NES_SACHEN_8259A)
+ SLOT_INTERFACE_INTERNAL("s8259b", NES_SACHEN_8259B)
+ SLOT_INTERFACE_INTERNAL("s8259c", NES_SACHEN_8259C)
+ SLOT_INTERFACE_INTERNAL("s8259d", NES_SACHEN_8259D)
+ SLOT_INTERFACE_INTERNAL("s74x374", NES_SACHEN_74X374)
+ SLOT_INTERFACE_INTERNAL("s74x374a", NES_SACHEN_74X374_ALT) /* FIXME: Made up boards some different handling */
+ SLOT_INTERFACE_INTERNAL("tcu01", NES_SACHEN_TCU01)
+ SLOT_INTERFACE_INTERNAL("tcu02", NES_SACHEN_TCU02)
+ SLOT_INTERFACE_INTERNAL("tengen_800008", NES_TENGEN_800008) /* FIXME: Is this the same as CNROM? */
+ SLOT_INTERFACE_INTERNAL("tengen_800032", NES_TENGEN_800032)
+ SLOT_INTERFACE_INTERNAL("tengen_800037", NES_TENGEN_800037)
+ SLOT_INTERFACE_INTERNAL("txc_22211", NES_TXC_22211)
+ SLOT_INTERFACE_INTERNAL("txc_dumarc", NES_TXC_DUMARACING)
+ SLOT_INTERFACE_INTERNAL("txc_mjblock", NES_TXC_MJBLOCK)
+ SLOT_INTERFACE_INTERNAL("txc_strikew", NES_TXC_STRIKEW)
+ SLOT_INTERFACE_INTERNAL("txc_commandos", NES_TXC_COMMANDOS)
+ SLOT_INTERFACE_INTERNAL("waixing_a", NES_WAIXING_A)
+ SLOT_INTERFACE_INTERNAL("waixing_a1", NES_WAIXING_A1) /* FIXME: Made up boards the different CHRRAM banks (see Ji Jia Zhan Shi) */
+ SLOT_INTERFACE_INTERNAL("waixing_b", NES_WAIXING_B)
+ SLOT_INTERFACE_INTERNAL("waixing_c", NES_WAIXING_C)
+ SLOT_INTERFACE_INTERNAL("waixing_d", NES_WAIXING_D)
+ SLOT_INTERFACE_INTERNAL("waixing_e", NES_WAIXING_E)
+ SLOT_INTERFACE_INTERNAL("waixing_f", NES_WAIXING_F)
+ SLOT_INTERFACE_INTERNAL("waixing_g", NES_WAIXING_G)
+ SLOT_INTERFACE_INTERNAL("waixing_h", NES_WAIXING_H)
+ SLOT_INTERFACE_INTERNAL("waixing_h1", NES_WAIXING_H1) /* FIXME: Made up boards the different WRAM protect banks (see Shen Mi Jin San Jiao) */
+ SLOT_INTERFACE_INTERNAL("waixing_i", NES_WAIXING_I)
+ SLOT_INTERFACE_INTERNAL("waixing_j", NES_WAIXING_J)
+ SLOT_INTERFACE_INTERNAL("waixing_sgz", NES_WAIXING_SGZ)
+ SLOT_INTERFACE_INTERNAL("waixing_sgzlz", NES_WAIXING_SGZLZ)
+ SLOT_INTERFACE_INTERNAL("waixing_sec", NES_WAIXING_SEC)
+ SLOT_INTERFACE_INTERNAL("waixing_ffv", NES_WAIXING_FFV)
+ SLOT_INTERFACE_INTERNAL("waixing_wxzs", NES_WAIXING_WXZS)
+ SLOT_INTERFACE_INTERNAL("waixing_wxzs2", NES_WAIXING_WXZS2)
+ SLOT_INTERFACE_INTERNAL("waixing_dq8", NES_WAIXING_DQ8)
+ SLOT_INTERFACE_INTERNAL("waixing_sh2", NES_WAIXING_SH2)
+ SLOT_INTERFACE_INTERNAL("fs304", NES_WAIXING_FS304) // used in Zelda 3 by Waixing
+ SLOT_INTERFACE_INTERNAL("cony", NES_CONY)
+ SLOT_INTERFACE_INTERNAL("yoko", NES_YOKO)
+ SLOT_INTERFACE_INTERNAL("hengg_srich", NES_HENGG_SRICH)
+ SLOT_INTERFACE_INTERNAL("hengg_xhzs", NES_HENGG_XHZS)
+ SLOT_INTERFACE_INTERNAL("hengg_shjy3", NES_HENGG_SHJY3) // mapper 253
+ SLOT_INTERFACE_INTERNAL("hes", NES_HES)
+ SLOT_INTERFACE_INTERNAL("hosenkan", NES_HOSENKAN)
+ SLOT_INTERFACE_INTERNAL("ks7058", NES_KS7058)
+ SLOT_INTERFACE_INTERNAL("ks202", NES_KS202) // mapper 56
+ SLOT_INTERFACE_INTERNAL("ks7022", NES_KS7022)// mapper 175
+ SLOT_INTERFACE_INTERNAL("ks7017", NES_KS7017)
+ SLOT_INTERFACE_INTERNAL("ks7032", NES_KS7032) // mapper 142
+ SLOT_INTERFACE_INTERNAL("ks7012", NES_KS7012) // used in Zanac (FDS Conversion)
+ SLOT_INTERFACE_INTERNAL("ks7013b", NES_KS7013B) // used in Highway Star (FDS Conversion)
+ SLOT_INTERFACE_INTERNAL("ks7031", NES_KS7031) // used in Dracula II (FDS Conversion)
+ SLOT_INTERFACE_INTERNAL("gs2015", NES_GS2015)
+ SLOT_INTERFACE_INTERNAL("gs2004", NES_GS2004)
+ SLOT_INTERFACE_INTERNAL("gs2013", NES_GS2013)
+ SLOT_INTERFACE_INTERNAL("tf9in1", NES_TF9IN1)
+ SLOT_INTERFACE_INTERNAL("3dblock", NES_3DBLOCK) // NROM + IRQ?
+ SLOT_INTERFACE_INTERNAL("racermate", NES_RACERMATE) // mapper 168
+ SLOT_INTERFACE_INTERNAL("agci_50282", NES_AGCI_50282)
+ SLOT_INTERFACE_INTERNAL("dreamtech01", NES_DREAMTECH01)
+ SLOT_INTERFACE_INTERNAL("fukutake", NES_FUKUTAKE)
+ SLOT_INTERFACE_INTERNAL("futuremedia", NES_FUTUREMEDIA)
+ SLOT_INTERFACE_INTERNAL("magicseries", NES_MAGSERIES)
+ SLOT_INTERFACE_INTERNAL("daou_306", NES_DAOU306)
+ SLOT_INTERFACE_INTERNAL("subor0", NES_SUBOR0)
+ SLOT_INTERFACE_INTERNAL("subor1", NES_SUBOR1)
+ SLOT_INTERFACE_INTERNAL("cc21", NES_CC21)
+ SLOT_INTERFACE_INTERNAL("xiaozy", NES_XIAOZY)
+ SLOT_INTERFACE_INTERNAL("edu2k", NES_EDU2K)
+ SLOT_INTERFACE_INTERNAL("t230", NES_T230)
+ SLOT_INTERFACE_INTERNAL("mk2", NES_MK2)
+ SLOT_INTERFACE_INTERNAL("unl_whero", NES_WHERO) // mapper 27
+ SLOT_INTERFACE_INTERNAL("unl_43272", NES_43272) // used in Gaau Hok Gwong Cheung
+ SLOT_INTERFACE_INTERNAL("tf1201", NES_TF1201)
+ SLOT_INTERFACE_INTERNAL("unl_cfight", NES_CITYFIGHT) // used by City Fighter IV
+// misc bootleg boards
+ SLOT_INTERFACE_INTERNAL("ax5705", NES_AX5705)
+ SLOT_INTERFACE_INTERNAL("sc127", NES_SC127)
+ SLOT_INTERFACE_INTERNAL("mariobaby", NES_MARIOBABY)
+ SLOT_INTERFACE_INTERNAL("asnicol", NES_ASN)
+ SLOT_INTERFACE_INTERNAL("smb3pirate", NES_SMB3PIRATE)
+ SLOT_INTERFACE_INTERNAL("btl_dninja", NES_BTL_DNINJA)
+ SLOT_INTERFACE_INTERNAL("whirl2706", NES_WHIRLWIND_2706)
+ SLOT_INTERFACE_INTERNAL("smb2j", NES_SMB2J)
+ SLOT_INTERFACE_INTERNAL("smb2ja", NES_SMB2JA)
+ SLOT_INTERFACE_INTERNAL("smb2jb", NES_SMB2JB)
+ SLOT_INTERFACE_INTERNAL("09034a", NES_09034A)
+ SLOT_INTERFACE_INTERNAL("tobidase", NES_TOBIDASE) // mapper 120
+ SLOT_INTERFACE_INTERNAL("mmalee2", NES_MMALEE) // mapper 55?
+ SLOT_INTERFACE_INTERNAL("unl_2708", NES_2708) // mapper 103
+ SLOT_INTERFACE_INTERNAL("unl_lh32", NES_LH32) // used by Monty no Doki Doki Daidassou FDS conversion
+ SLOT_INTERFACE_INTERNAL("unl_lh10", NES_LH10) // used in Fuuun Shaolin Kyo (FDS Conversion)
+ SLOT_INTERFACE_INTERNAL("unl_lh53", NES_LH53) // used in Nazo no Murasamejou (FDS Conversion)
+ SLOT_INTERFACE_INTERNAL("unl_ac08", NES_AC08) // used by Green Beret FDS conversion
+ SLOT_INTERFACE_INTERNAL("unl_bb", NES_UNL_BB) // used by a few FDS conversions
+ SLOT_INTERFACE_INTERNAL("sgpipe", NES_SHUIGUAN) // mapper 183
+// misc MMC3 clone boards
+ SLOT_INTERFACE_INTERNAL("dbz5", NES_REX_DBZ5)
+ SLOT_INTERFACE_INTERNAL("sl1632", NES_REX_SL1632)
+ SLOT_INTERFACE_INTERNAL("somari", NES_SOMARI) // mapper 116
+ SLOT_INTERFACE_INTERNAL("nitra", NES_NITRA)
+ SLOT_INTERFACE_INTERNAL("ks7057", NES_KS7057) // mapper 196 alt (for Street Fighter VI / Fight Street VI)
+ SLOT_INTERFACE_INTERNAL("sbros11", NES_SBROS11)
+ SLOT_INTERFACE_INTERNAL("unl_malisb", NES_MALISB) // used by Super Mali Splash Bomb
+ SLOT_INTERFACE_INTERNAL("family4646", NES_FAMILY4646)
+ SLOT_INTERFACE_INTERNAL("pikay2k", NES_PIKAY2K) // mapper 254
+ SLOT_INTERFACE_INTERNAL("8237", NES_8237)
+ SLOT_INTERFACE_INTERNAL("8237a", NES_NROM) // UNSUPPORTED
+ SLOT_INTERFACE_INTERNAL("sg_lionk", NES_SG_LIONK)
+ SLOT_INTERFACE_INTERNAL("sg_boog", NES_SG_BOOG)
+ SLOT_INTERFACE_INTERNAL("kasing", NES_KASING)
+ SLOT_INTERFACE_INTERNAL("kay", NES_KAY)
+ SLOT_INTERFACE_INTERNAL("h2288", NES_H2288)
+ SLOT_INTERFACE_INTERNAL("unl_6035052", NES_6035052) // mapper 238?
+ SLOT_INTERFACE_INTERNAL("txc_tw", NES_TXC_TW)
+ SLOT_INTERFACE_INTERNAL("kof97", NES_KOF97)
+ SLOT_INTERFACE_INTERNAL("kof96", NES_KOF96)
+ SLOT_INTERFACE_INTERNAL("sfight3", NES_SF3)
+ SLOT_INTERFACE_INTERNAL("gouder", NES_GOUDER)
+ SLOT_INTERFACE_INTERNAL("sa9602b", NES_SA9602B)
+ SLOT_INTERFACE_INTERNAL("unl_shero", NES_SACHEN_SHERO)
+// misc multigame cart boards
+ SLOT_INTERFACE_INTERNAL("benshieng", NES_BENSHIENG)
+ SLOT_INTERFACE_INTERNAL("action52", NES_ACTION52)
+ SLOT_INTERFACE_INTERNAL("caltron6in1", NES_CALTRON6IN1)
+ SLOT_INTERFACE_INTERNAL("maxi15", NES_MAXI15) // mapper 234
+ SLOT_INTERFACE_INTERNAL("rumblestation", NES_RUMBLESTATION) // mapper 46
+ SLOT_INTERFACE_INTERNAL("svision16", NES_SVISION16) // mapper 53
+ SLOT_INTERFACE_INTERNAL("n625092", NES_N625092)
+ SLOT_INTERFACE_INTERNAL("a65as", NES_A65AS)
+ SLOT_INTERFACE_INTERNAL("t262", NES_T262)
+ SLOT_INTERFACE_INTERNAL("novel1", NES_NOVEL1)
+ SLOT_INTERFACE_INTERNAL("novel2", NES_NOVEL2) // mapper 213... same as BMC-NOVELDIAMOND9999999IN1 board?
+ SLOT_INTERFACE_INTERNAL("studyngame", NES_STUDYNGAME) // mapper 39
+ SLOT_INTERFACE_INTERNAL("sgun20in1", NES_SUPERGUN20IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_vt5201", NES_VT5201) // mapper 60 otherwise
+ SLOT_INTERFACE_INTERNAL("bmc_d1038", NES_VT5201) // mapper 60?
+ SLOT_INTERFACE_INTERNAL("810544c", NES_810544C)
+ SLOT_INTERFACE_INTERNAL("ntd03", NES_NTD03)
+ SLOT_INTERFACE_INTERNAL("bmc_gb63", NES_BMC_GB63)
+ SLOT_INTERFACE_INTERNAL("bmc_gka", NES_BMC_GKA)
+ SLOT_INTERFACE_INTERNAL("bmc_gkb", NES_BMC_GKB)
+ SLOT_INTERFACE_INTERNAL("bmc_ws", NES_BMC_WS)
+ SLOT_INTERFACE_INTERNAL("bmc_g146", NES_BMC_G146)
+ SLOT_INTERFACE_INTERNAL("bmc_11160", NES_BMC_11160)
+ SLOT_INTERFACE_INTERNAL("bmc_8157", NES_BMC_8157)
+ SLOT_INTERFACE_INTERNAL("bmc_hik300", NES_BMC_HIK300)
+ SLOT_INTERFACE_INTERNAL("bmc_s700", NES_BMC_S700)
+ SLOT_INTERFACE_INTERNAL("bmc_ball11", NES_BMC_BALL11)
+ SLOT_INTERFACE_INTERNAL("bmc_22games", NES_BMC_22GAMES)
+ SLOT_INTERFACE_INTERNAL("bmc_64y2k", NES_BMC_64Y2K)
+ SLOT_INTERFACE_INTERNAL("bmc_12in1", NES_BMC_12IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_20in1", NES_BMC_20IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_21in1", NES_BMC_21IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_31in1", NES_BMC_31IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_35in1", NES_BMC_35IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_36in1", NES_BMC_36IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_64in1", NES_BMC_64IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_70in1", NES_BMC_70IN1) // mapper 236?
+ SLOT_INTERFACE_INTERNAL("bmc_72in1", NES_BMC_72IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_76in1", NES_BMC_76IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_s42in1", NES_BMC_76IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_110in1", NES_BMC_110IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_150in1", NES_BMC_150IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_190in1", NES_BMC_190IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_800in1", NES_BMC_800IN1) // mapper 236?
+ SLOT_INTERFACE_INTERNAL("bmc_1200in1", NES_BMC_1200IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_gold150", NES_BMC_GOLD150) // mapper 235 with 2M PRG
+ SLOT_INTERFACE_INTERNAL("bmc_gold260", NES_BMC_GOLD260) // mapper 235 with 4M PRG
+ SLOT_INTERFACE_INTERNAL("bmc_power255", NES_BMC_CH001) // mapper 63?
+ SLOT_INTERFACE_INTERNAL("bmc_s22games", NES_BMC_SUPER22) // mapper 233
+ SLOT_INTERFACE_INTERNAL("bmc_reset4", NES_BMC_4IN1RESET) // mapper 60 with 64k prg and 32k chr
+ SLOT_INTERFACE_INTERNAL("bmc_reset42", NES_BMC_42IN1RESET) // mapper 60? or 226? or 233?
+// misc multigame cart MMC3 clone boards
+ SLOT_INTERFACE_INTERNAL("fk23c", NES_FK23C)
+ SLOT_INTERFACE_INTERNAL("fk23ca", NES_FK23CA)
+ SLOT_INTERFACE_INTERNAL("s24in1c03", NES_S24IN1SC03)
+ SLOT_INTERFACE_INTERNAL("bmc_15in1", NES_BMC_15IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_sbig7in1", NES_BMC_SBIG7)
+ SLOT_INTERFACE_INTERNAL("bmc_hik8in1", NES_BMC_HIK8)
+ SLOT_INTERFACE_INTERNAL("bmc_hik4in1", NES_BMC_HIK4)
+ SLOT_INTERFACE_INTERNAL("bmc_mario7in1", NES_BMC_MARIO7IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_gold7in1", NES_BMC_GOLD7IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_gc6in1", NES_BMC_GC6IN1)
+ SLOT_INTERFACE_INTERNAL("bmc_411120c", NES_BMC_411120C)
+ SLOT_INTERFACE_INTERNAL("bmc_830118c", NES_BMC_830118C)
+ SLOT_INTERFACE_INTERNAL("pjoy84", NES_PJOY84)
+ SLOT_INTERFACE_INTERNAL("nocash_nochr", NES_NOCHR)
+ SLOT_INTERFACE_INTERNAL("nes_action53", NES_ACTION53)
+ SLOT_INTERFACE_INTERNAL("nes_2a03pur", NES_2A03PURITANS)
+// other unsupported...
+ SLOT_INTERFACE_INTERNAL("ninjaryu", NES_NROM) // mapper 111 - UNSUPPORTED
+ SLOT_INTERFACE_INTERNAL("unl_dance", NES_NROM) // UNSUPPORTED
+ SLOT_INTERFACE_INTERNAL("onebus", NES_NROM) // UNSUPPORTED
+ SLOT_INTERFACE_INTERNAL("dance2k", NES_NROM) // UNSUPPORTED
+ SLOT_INTERFACE_INTERNAL("pec586", NES_NROM) // UNSUPPORTED
+// are there dumps of games with these boards?
+ SLOT_INTERFACE_INTERNAL("bmc_hik_kof", NES_NROM) // mapper 251 - UNSUPPORTED
+ SLOT_INTERFACE_INTERNAL("bmc_13in1jy110", NES_NROM) // [mentioned in FCEUMM source - we need more info] - UNSUPPORTED
+ SLOT_INTERFACE_INTERNAL("bmc_gk_192", NES_NROM) // [mentioned in FCEUMM source - we need more info] - UNSUPPORTED
+ SLOT_INTERFACE_INTERNAL("konami_qtai", NES_NROM) // [mentioned in FCEUMM source - we need more info] - UNSUPPORTED
+ SLOT_INTERFACE_INTERNAL("unl_3d_block", NES_NROM) // [mentioned in FCEUMM source - we need more info] - UNSUPPORTED
+ SLOT_INTERFACE_INTERNAL("unl_c_n22m", NES_NROM) // [mentioned in FCEUMM source - we need more info] - UNSUPPORTED
+ SLOT_INTERFACE_INTERNAL("a9746", NES_NROM) // mapper 219 - UNSUPPORTED (no dump available)
+// legacy boards for FFE copier mappers (are there images available to fix/improve emulation?)
+ SLOT_INTERFACE_INTERNAL("ffe3", NES_FFE3)
+ SLOT_INTERFACE_INTERNAL("ffe4", NES_FFE4)
+ SLOT_INTERFACE_INTERNAL("ffe8", NES_FFE8)
+ SLOT_INTERFACE_INTERNAL("test", NES_NROM)
+//
+ SLOT_INTERFACE_INTERNAL("unknown", NES_NROM) // a few pirate dumps uses the wrong mapper...
+SLOT_INTERFACE_END
+
+SLOT_INTERFACE_START(disksys_only)
+ // RAM expansion + Disk System add-on
+ SLOT_INTERFACE("disksys", NES_DISKSYS)
+SLOT_INTERFACE_END
diff --git a/src/devices/bus/nes/nes_carts.h b/src/devices/bus/nes/nes_carts.h
new file mode 100644
index 00000000000..144d7aa0a8e
--- /dev/null
+++ b/src/devices/bus/nes/nes_carts.h
@@ -0,0 +1,73 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/**********************************************************************
+
+ NES carts
+
+**********************************************************************/
+
+#pragma once
+
+#ifndef __NES_CARTS_H__
+#define __NES_CARTS_H__
+
+#include "emu.h"
+
+// official PCBs
+#include "nxrom.h"
+#include "mmc1.h"
+#include "mmc2.h"
+#include "mmc3.h"
+#include "mmc5.h"
+#include "bandai.h"
+#include "datach.h"
+#include "discrete.h"
+#include "disksys.h"
+#include "event.h"
+#include "irem.h"
+#include "jaleco.h"
+#include "karastudio.h"
+#include "konami.h"
+#include "namcot.h"
+#include "pt554.h"
+#include "sunsoft.h"
+#include "sunsoft_dcs.h"
+#include "taito.h"
+// unlicensed/bootleg/pirate PCBs
+#include "2a03pur.h"
+#include "act53.h"
+#include "aladdin.h"
+#include "ave.h"
+#include "benshieng.h"
+#include "camerica.h"
+#include "cne.h"
+#include "cony.h"
+#include "ggenie.h"
+#include "hes.h"
+#include "henggedianzi.h"
+#include "hosenkan.h"
+#include "jy.h"
+#include "kaiser.h"
+#include "legacy.h"
+#include "nanjing.h"
+#include "ntdec.h"
+#include "racermate.h"
+#include "rcm.h"
+#include "rexsoft.h"
+#include "sachen.h"
+#include "somari.h"
+#include "tengen.h"
+#include "txc.h"
+#include "waixing.h"
+// misc unlicensed/bootleg/pirate PCBs
+#include "bootleg.h"
+#include "multigame.h"
+#include "pirate.h"
+#include "mmc3_clones.h"
+
+
+// supported devices
+SLOT_INTERFACE_EXTERN(nes_cart);
+SLOT_INTERFACE_EXTERN(disksys_only);
+
+#endif // __NES_CARTS_H__
diff --git a/src/devices/bus/nes/nes_ines.inc b/src/devices/bus/nes/nes_ines.inc
new file mode 100644
index 00000000000..ceb9581b82f
--- /dev/null
+++ b/src/devices/bus/nes/nes_ines.inc
@@ -0,0 +1,939 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/*****************************************************************************************
+
+ NES MMC Emulation
+
+ Support for iNES Mappers
+
+****************************************************************************************/
+
+
+/* Set to generate prg & chr files when the cart is loaded */
+#define SPLIT_PRG 0
+#define SPLIT_CHR 0
+
+
+/*************************************************************
+
+ mmc_list
+
+ Supported mappers and corresponding pcb id
+
+ *************************************************************/
+
+struct nes_mmc
+{
+ int iNesMapper; /* iNES Mapper # */
+ int pcb_id;
+};
+
+
+static const nes_mmc mmc_list[] =
+{
+/* INES DESC LOW_W, LOW_R, MED_W, HIGH_W, PPU_latch, scanline CB, hblank CB */
+ { 0, STD_NROM },
+ { 1, STD_SXROM },
+ { 2, STD_UXROM },
+ { 3, STD_CNROM },
+ { 4, STD_TXROM },
+ { 5, STD_EXROM },
+ { 6, FFE4_BOARD },
+ { 7, STD_AXROM },
+ { 8, FFE3_BOARD },
+ { 9, STD_PXROM },
+ { 10, STD_FXROM },
+ { 11, DIS_74X377 },
+ { 12, REXSOFT_DBZ5 },
+ { 13, STD_CPROM },
+ { 14, REXSOFT_SL1632 },
+ { 15, WAIXING_WXZS2 },
+ { 16, BANDAI_LZ93EX2 }, // with 24c02
+ { 17, FFE8_BOARD },
+ { 18, JALECO_SS88006 },
+ { 19, NAMCOT_163 },
+ { 21, KONAMI_VRC4 },
+ { 22, KONAMI_VRC2 },
+ { 23, KONAMI_VRC2 },
+ { 24, KONAMI_VRC6 },
+ { 25, KONAMI_VRC4 },
+ { 26, KONAMI_VRC6 },
+ { 27, UNL_WORLDHERO }, // 27 World Hero board - Unsupported
+ { 28, BTL_ACTION53 }, // 28 - Multi-discrete PCB designed by Tepples for Action 53
+ // 29 Unused
+ // 30 UNROM 512 + Flash, currently unsupported
+ { 31, BTL_2A03_PURITANS }, // 31 - PCB designed by infinitelives & rainwarrior for 2A03 Puritans Album
+ { 32, IREM_G101 },
+ { 33, TAITO_TC0190FMC },
+ { 34, STD_BXROM },
+ { 35, UNL_SC127 },
+ { 36, TXC_STRIKEW },
+ { 37, PAL_ZZ },
+ { 38, DIS_74X161X138 },
+ { 39, UNL_STUDYNGAME },
+ { 40, BTL_SMB2JA },
+ { 41, CALTRON_6IN1 },
+ { 42, BTL_MARIOBABY }, // ai senshi nicole too, changed by crc_hack
+ { 43, UNL_SMB2J },
+ { 44, BMC_SUPERBIG_7IN1 },
+ { 45, BMC_HIK8IN1 },
+ { 46, RUMBLESTATION_BOARD },
+ { 47, NES_QJ },
+ { 48, TAITO_TC0190FMCP },
+ { 49, BMC_SUPERHIK_4IN1 },
+ { 50, BTL_SMB2JB },
+ { 51, BMC_BALLGAMES_11IN1 },
+ { 52, BMC_GOLD_7IN1 },
+ { 53, SVISION16_BOARD },
+ { 54, BMC_NOVEL1 },
+ // 55 Genius SMB - No info (nor images) available
+ { 56, KAISER_KS202 },
+ { 57, BMC_GKA },
+ { 58, BMC_GKB },
+ // 59 Unused
+ // 60 4-in-1, 35-in-1 Reset based
+ { 61, RCM_TF9IN1 },
+ { 62, BMC_SUPER_700IN1 },
+ { 63, BMC_CH001 }, // Powerful 255
+ { 64, TENGEN_800032 },
+ { 65, IREM_H3001 },
+ { 66, STD_GXROM },
+ { 67, SUNSOFT_3 },
+ { 68, SUNSOFT_DCS },
+ { 69, SUNSOFT_FME7 },
+ { 70, DIS_74X161X161X32 },
+ { 71, CAMERICA_BF9093 },
+ { 72, JALECO_JF17 },
+ { 73, KONAMI_VRC3 },
+ { 74, WAIXING_TYPE_A },
+ { 75, KONAMI_VRC1 },
+ { 76, NAMCOT_3446 },
+ { 77, IREM_LROG017 },
+ { 78, IREM_HOLYDIVR },
+ { 79, AVE_NINA06 },
+ { 80, TAITO_X1_005 },
+ // 81 Unused
+ { 82, TAITO_X1_017 },
+ { 83, CONY_BOARD },
+ // 84 Pasofami hacked images?
+ { 85, KONAMI_VRC7 },
+ { 86, JALECO_JF13 },
+ { 87, DIS_74X139X74 },
+ { 88, NAMCOT_34X3 },
+ { 89, SUNSOFT_2 },
+ { 90, JYCOMPANY_A },
+ { 91, UNL_MK2 },
+ { 92, JALECO_JF19 },
+ { 93, SUNSOFT_2 },
+ { 94, STD_UN1ROM },
+ { 95, NAMCOT_3425 },
+ { 96, BANDAI_OEKAKIDS },
+ { 97, IREM_TAM_S1 },
+ // 98 Unused
+ // 99 VS. system - Not going to be implemented (use MAME instead)
+ // 100 images hacked to work with nesticle?
+ // 101 Unused (Urusei Yatsura had been assigned to this mapper, but it's Mapper 87)
+ // 102 Unused
+ { 103, UNL_2708 }, // 103 Bootleg cart 2708 (Doki Doki Panic - FDS Conversion) - Unsupported
+ { 104, CAMERICA_GOLDENFIVE },
+ { 105, STD_EVENT },
+ { 106, BTL_SMB3 },
+ { 107, MAGICSERIES_MD },
+ { 108, WHIRLWIND_2706 },
+ // 109 Unused
+ // 110 Unused
+ // 111 Ninja Ryuukenden Chinese? - Unsupported
+ { 112, NTDEC_ASDER },
+ { 113, HES_BOARD },
+ { 114, SUPERGAME_LIONKING },
+ { 115, KASING_BOARD },
+ { 116, SOMARI_SL12 },
+ { 117, FUTUREMEDIA_BOARD },
+ { 118, STD_TXSROM },
+ { 119, STD_TQROM },
+ { 120, BTL_TOBIDASE },
+ { 121, KAY_BOARD },
+ // 122 Unused
+ { 123, UNL_H2288 },
+ // 124 Unused
+ // 125 Unused
+ { 126, BMC_PJOY84 },
+ // 127 Unused
+ // 128 Unused
+ // 129 Unused
+ // 130 Unused
+ // 131 Unused
+ { 132, TXC_22211 },
+ { 133, SACHEN_SA72008 },
+ { 134, BMC_FAMILY_4646 },
+ // 135 Unused
+ { 136, SACHEN_TCU02 },
+ { 137, SACHEN_8259D },
+ { 138, SACHEN_8259B },
+ { 139, SACHEN_8259C },
+ { 140, JALECO_JF11 },
+ { 141, SACHEN_8259A },
+ { 142, KAISER_KS7032},
+ { 143, SACHEN_TCA01 },
+ { 144, AGCI_50282 },
+ { 145, SACHEN_SA72007 },
+ { 146, AVE_NINA06 }, // basically same as Mapper 79 (Nina006)
+ { 147, SACHEN_TCU01 },
+ { 148, SACHEN_SA0037 },
+ { 149, SACHEN_SA0036 },
+ { 150, SACHEN_74LS374 },
+ // 151 VS. system by Konami - Not going to be implemented (use MAME instead)
+ { 152, DIS_74X161X161X32 },
+ { 153, BANDAI_LZ93 },
+ { 154, NAMCOT_34X3 },
+ { 155, STD_SXROM_A }, // diff compared to MMC1 concern WRAM
+ { 156, OPENCORP_DAOU306 },
+ { 157, BANDAI_DATACH }, // Datach Reader games -> must go in the Datach subslot
+ { 158, TENGEN_800037 },
+ { 159, BANDAI_LZ93EX1 }, // with 24c01
+ { 160, SACHEN_SA009 },
+ // 161 Unused
+ { 162, WAIXING_FS304}, // not confirmed, but a lot of chinese releases use it like this...
+ { 163, NANJING_BOARD},
+ { 164, WAIXING_FFV },
+ { 165, WAIXING_SH2 },
+ { 166, SUBOR_TYPE1 },
+ { 167, SUBOR_TYPE0 },
+ { 168, UNL_RACERMATE },
+ // 169 Unused
+ // 170 Fujiya
+ { 171, KAISER_KS7058 },
+ { 172, TXC_DUMARACING },
+ { 173, TXC_MJBLOCK },
+ // 174 Unused
+ { 175, KAISER_KS7022},
+ { 176, UNL_XIAOZY },
+ { 177, HENGG_SRICH },
+ { 178, WAIXING_SGZLZ },
+ { 179, HENGG_XHZS },
+ { 180, UXROM_CC },
+ // 181 Unused
+ { 182, HOSENKAN_BOARD },
+ { 183, BTL_SHUIGUAN },
+ { 184, SUNSOFT_1 },
+ { 185, STD_CNROM },
+ { 186, FUKUTAKE_BOARD },
+ { 187, UNL_KOF96 },
+ { 188, BANDAI_KARAOKE },
+ { 189, TXC_TW },
+ // 190 Unused
+ { 191, WAIXING_TYPE_B },
+ { 192, WAIXING_TYPE_C },
+ { 193, NTDEC_FIGHTINGHERO },
+ { 194, WAIXING_TYPE_D },
+ { 195, WAIXING_TYPE_E },
+ { 196, BTL_SBROS11 },
+ { 197, UNL_SF3 },
+ { 198, WAIXING_TYPE_F },
+ { 199, WAIXING_TYPE_G },
+ { 200, BMC_36IN1 },
+ { 201, BMC_21IN1 },
+ { 202, BMC_150IN1 },
+ { 203, BMC_35IN1 },
+ { 204, BMC_64IN1 },
+ { 205, BMC_15IN1 },
+ { 206, NAMCOT_34X3 },
+ { 207, TAITO_X1_005 },
+ { 208, GOUDER_37017 },
+ { 209, JYCOMPANY_C },
+ { 210, NAMCOT_175 },
+ { 211, JYCOMPANY_B },
+ { 212, BMC_SUPERHIK_300IN1 },
+ { 213, BMC_NOVEL2 },
+ { 214, BMC_SUPERGUN_20IN1 },
+ { 215, SUPERGAME_BOOGERMAN },
+ { 216, RCM_GS2015 },
+ { 217, BMC_GOLDENCARD_6IN1 },
+ { 218, NOCASH_NOCHR },
+ // 219 UNL-A9746 (according to Cah4e3's code, no dump available (yet)
+ // 220 Unused
+ { 221, UNL_N625092 },
+ { 222, BTL_DRAGONNINJA },
+ { 223, WAIXING_TYPE_I }, // (according to NEStopia source, it's MMC3 with more WRAM)
+ { 224, WAIXING_TYPE_J }, // (according to NEStopia source, it's MMC3 with more WRAM)
+ { 225, BMC_72IN1 },
+ { 226, BMC_76IN1 },
+ { 227, BMC_1200IN1 },
+ { 228, ACTENT_ACT52 },
+ { 229, BMC_31IN1 },
+ { 230, BMC_22GAMES },
+ { 231, BMC_20IN1 },
+ { 232, CAMERICA_BF9096 },
+ { 233, BMC_SUPER22 },
+ { 234, AVE_MAXI15 },
+ { 235, BMC_GOLD150 }, // 235 Golden Game x-in-1 - Unsupported
+ // 236 Game 800-in-1 - Unsupported
+ // 237 Unused
+ { 238, UNL_603_5052 },
+ // 239 Unused
+ { 240, CNE_SHLZ },
+ { 241, TXC_COMMANDOS },
+ { 242, WAIXING_WXZS },
+ { 243, SACHEN_74LS374_ALT },
+ { 244, CNE_DECATHLON },
+ { 245, WAIXING_TYPE_H },
+ { 246, CNE_FSB },
+ // 247 Unused
+ // 248 Unused
+ { 249, WAIXING_SECURITY },
+ { 250, NITRA_TDA },
+ // 251 Shen Hua Jian Yun III?? - Unsupported
+ { 252, WAIXING_SGZ },
+ // 253 Super 8-in-1 99 King Fighter?? - Unsupported
+ { 254, BTL_PIKACHUY2K },
+ { 255, BMC_110IN1 },
+};
+
+const nes_mmc *nes_mapper_lookup( int mapper )
+{
+ int i;
+
+ for (i = 0; i < ARRAY_LENGTH(mmc_list); i++)
+ {
+ if (mmc_list[i].iNesMapper == mapper)
+ return &mmc_list[i];
+ }
+
+ return NULL;
+}
+
+#if 0
+int nes_get_mmc_id( running_machine &machine, int mapper )
+{
+ const nes_mmc *mmc = nes_mapper_lookup(mapper);
+
+ if (mmc == NULL)
+ fatalerror("Unimplemented Mapper %d\n", mapper);
+
+ return mmc->pcb_id;
+}
+#endif
+
+/*************************************************************
+
+ ines_mapr_setup
+
+ setup the board specific pcb_id for a given mapper
+
+ *************************************************************/
+
+void ines_mapr_setup( int mapper, int *pcb_id )
+{
+ const nes_mmc *mmc = nes_mapper_lookup(mapper);
+ if (mmc == NULL)
+ fatalerror("Unimplemented Mapper %d\n", mapper);
+
+ *pcb_id = mmc->pcb_id;
+}
+
+/*************************************************************
+
+ call_load_ines
+
+ *************************************************************/
+
+void nes_cart_slot_device::call_load_ines()
+{
+ UINT32 vram_size = 0, prgram_size = 0, battery_size = 0, mapper_sram_size = 0;
+ UINT32 prg_size, vrom_size;
+ UINT8 header[0x10];
+ UINT8 mapper, submapper = 0, local_options;
+ bool ines20 = FALSE, prg16k;
+ std::string mapinfo;
+ int pcb_id = 0, mapint1 = 0, mapint2 = 0, mapint3 = 0, mapint4 = 0;
+ int crc_hack = 0;
+ bool bus_conflict = FALSE;
+
+ // read out the header
+ fseek(0, SEEK_SET);
+ fread(&header, 0x10);
+
+ // SETUP step 1: getting PRG, VROM, VRAM sizes
+ prg16k = (header[4] == 1);
+ prg_size = prg16k ? 2 * 0x4000 : header[4] * 0x4000;
+ vrom_size = header[5] * 0x2000;
+ vram_size = 0x4000;
+
+ // SETUP step 2: getting PCB and other settings
+ mapper = (header[6] & 0xf0) >> 4;
+ local_options = header[6] & 0x0f;
+
+ switch (header[7] & 0xc)
+ {
+ case 0x4:
+ case 0xc:
+ // probably the header got corrupted: don't trust upper bits for mapper
+ break;
+
+ case 0x8: // it's iNES 2.0 format
+ ines20 = TRUE;
+ case 0x0:
+ default:
+ mapper |= header[7] & 0xf0;
+ break;
+ }
+
+ // use info from nes.hsi if available!
+ if (hashfile_extrainfo(*this, mapinfo))
+ {
+ if (4 == sscanf(mapinfo.c_str(),"%d %d %d %d", &mapint1, &mapint2, &mapint3, &mapint4))
+ {
+ /* image is present in nes.hsi: overwrite the header settings with these */
+ mapper = mapint1;
+ local_options = mapint2 & 0x0f;
+ crc_hack = (mapint2 & 0xf0) >> 4; // this is used to differentiate among variants of the same Mapper (see below)
+ prg16k = (mapint3 == 1);
+ prg_size = prg16k ? 2 * 0x4000 : mapint3 * 0x4000;
+ vrom_size = mapint4 * 0x2000;
+ logerror("NES.HSI info: %d %d %d %d\n", mapint1, mapint2, mapint3, mapint4);
+ }
+ else
+ {
+ logerror("NES: [%s], Invalid mapinfo found\n", mapinfo.c_str());
+ }
+ }
+ else
+ {
+ logerror("NES: No extrainfo found\n");
+ }
+
+ // use extended iNES2.0 info if available!
+ if (ines20)
+ {
+ mapper |= (header[8] & 0x0f) << 8;
+ // read submappers (based on 20140116 specs)
+ submapper = (header[8] & 0xf0 >> 8);
+ prg_size += ((header[9] & 0x0f) << 8) * 0x4000;
+ vrom_size += ((header[9] & 0xf0) << 4) * 0x2000;
+ }
+ ines_mapr_setup(mapper, &pcb_id);
+
+ // handle submappers
+ if (submapper)
+ {
+ // 001: MMC1
+ if (mapper == 1 && submapper == 3)
+ pcb_id = STD_SXROM_A;
+ else if (mapper == 1 && submapper == 5)
+ logerror("Unimplemented iNES2.0 submapper: SEROM/SHROM/SH1ROM.\n");
+ // 002, 003, 007: UxROM, CNROM, AxROM
+ else if (mapper == 2 && submapper == 2)
+ bus_conflict = TRUE;
+ else if (mapper == 3 && submapper == 2)
+ bus_conflict = TRUE;
+ else if (mapper == 7 && submapper == 2)
+ bus_conflict = TRUE;
+ // 021, 023, 025: VRC4 / VRC2
+ else if (mapper == 21 || mapper == 23 || mapper == 25)
+ {
+ // 021, 023, 025: VRC4
+ int line_1 = submapper & 0x07;
+ int line_2 = (submapper & 0x08) ? line_1 + 1 : line_1 - 1;
+ if (line_2 >= 0 && line_2 <= 7)
+ {
+ pcb_id = KONAMI_VRC4;
+ m_cart->set_vrc_lines(line_1, line_2, 0);
+ }
+ else if (submapper == 15)
+ {
+ pcb_id = KONAMI_VRC2;
+ m_cart->set_vrc_lines(1, 0, 0);
+ }
+ }
+ // 032: Irem G101
+ else if (mapper == 32 && submapper == 1)
+ {
+ m_cart->set_mirroring(PPU_MIRROR_HIGH); // Major League has hardwired mirroring
+ }
+ // iNES Mapper 034
+ else if (mapper == 34 && submapper == 1)
+ {
+ pcb_id = AVE_NINA01; // Mapper 34 is used for 2 diff boards
+ }
+ // iNES Mapper 068 / Sunsoft 4
+ else if (mapper == 68 && submapper == 1)
+ {
+ submapper = 0;
+ logerror("Unimplemented iNES2.0 submapper: SUNSOFT-DCS.\n");
+ }
+ // iNES Mapper 071
+ else if (mapper == 71 && submapper == 1)
+ {
+ m_cart->set_pcb_ctrl_mirror(TRUE); // Mapper 71 is used for 2 diff boards
+ }
+ // iNES Mapper 078
+ else if (mapper == 78)
+ {
+ if (submapper == 1)
+ pcb_id = JALECO_JF16; // Mapper 78 is used for 2 diff boards
+ else if (submapper == 3)
+ pcb_id = IREM_HOLYDIVR;
+ }
+ // iNES Mapper 185
+ else if (mapper == 185)
+ {
+ int ce_state = (submapper & 0x0c) >> 2;
+ m_cart->set_ce(0x03, ce_state);
+ }
+ // iNES Mapper 232
+ else if (mapper == 210 && submapper == 1)
+ {
+ submapper = 0;
+ logerror("Unimplemented iNES2.0 submapper: CAMERICA-BF9096.\n");
+ }
+ else if (submapper)
+ {
+ submapper = 0;
+ logerror("Undocumented iNES2.0 submapper, please report it to the MESS boards!\n");
+ }
+ }
+
+ // SETUP step 3: storing the info needed for emulation
+ m_pcb_id = pcb_id;
+ m_cart->set_mirroring(BIT(local_options, 0) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ);
+ if (BIT(local_options, 1))
+ battery_size = NES_BATTERY_SIZE; // with original iNES format we can only support 8K WRAM battery
+ m_cart->set_trainer(BIT(local_options, 2) ? TRUE : FALSE);
+ m_cart->set_four_screen_vram(BIT(local_options, 3) ? TRUE : FALSE);
+
+ if (ines20)
+ {
+ // PRGRAM/BWRAM (not fully supported, also due to lack of 2.0 files)
+ if ((header[10] & 0x0f) > 0)
+ prgram_size = 0x80 << ((header[10] & 0x0f) - 1);
+ if ((header[10] & 0xf0) > 0)
+ battery_size = 0x80 << (((header[10] & 0xf0) >> 4) - 1);
+ // VRAM
+ vram_size = 0;
+ if ((header[11] & 0x0f) > 0)
+ vram_size = 0x80 << ((header[11] & 0x0f) - 1);
+ if ((header[11] & 0xf0) > 0)
+ vram_size |= 0x80 << (((header[11] & 0xf0) >> 4) - 1);
+ // header[11] & 0xf0 is the size of battery backed VRAM, found so far in Racermate II only and not supported yet
+ }
+ else
+ {
+ // PRGRAM size is 8k for most games, but pirate carts often use different sizes,
+ // so its size has been added recently to the iNES format spec, but almost no image uses it
+ prgram_size = header[8] ? header[8] * 0x2000 : 0x2000;
+ }
+
+ // a few mappers correspond to multiple PCBs, so we need a few additional checks and tweaks
+ switch (m_pcb_id)
+ {
+ case STD_NROM:
+ if (prg_size == 3 * 0x4000) // NROM368 are padded with 2k empty data at start to accomplish with iNES standard
+ {
+ m_pcb_id = STD_NROM368;
+ fseek(0x810, SEEK_SET);
+ prg_size = 0xb800;
+ }
+ break;
+
+ case NOCASH_NOCHR:
+ // this mapper uses mirroring flags differently
+ m_cart->set_four_screen_vram(FALSE);
+ switch (local_options & 0x09)
+ {
+ case 0x00:
+ m_cart->set_mirroring(PPU_MIRROR_HORZ);
+ break;
+ case 0x01:
+ m_cart->set_mirroring(PPU_MIRROR_VERT);
+ break;
+ case 0x08:
+ m_cart->set_mirroring(PPU_MIRROR_LOW);
+ break;
+ case 0x09:
+ m_cart->set_mirroring(PPU_MIRROR_HIGH);
+ break;
+ }
+ break;
+
+ case STD_CNROM:
+ if (mapper == 185 && !submapper)
+ {
+ switch (crc_hack)
+ {
+ case 0x0: // pin26: CE, pin27: CE (B-Wings, Bird Week)
+ m_cart->set_ce(0x03, 0x03);
+ break;
+ case 0x4: // pin26: CE, pin27: /CE (Mighty Bomb Jack, Spy Vs. Spy)
+ m_cart->set_ce(0x03, 0x01);
+ break;
+ case 0x8: // pin26: /CE, pin27: CE (Sansu 1, 2, 3 Nen, Othello)
+ m_cart->set_ce(0x03, 0x02);
+ break;
+ case 0xc: // pin26: /CE, pin27: /CE (Seicross v2.0)
+ m_cart->set_ce(0x03, 0x00);
+ break;
+ }
+ }
+ break;
+
+ case KONAMI_VRC2:
+ if (mapper == 22)
+ m_cart->set_vrc_lines(0, 1, 1);
+ if (mapper == 23 && !crc_hack && !submapper)
+ m_cart->set_vrc_lines(1, 0, 0);
+ if (mapper == 23 && crc_hack && !submapper)
+ {
+ // here there are also Akumajou Special, Crisis Force, Parodius da!, Tiny Toons which are VRC-4
+ m_cart->set_vrc_lines(3, 2, 0);
+ m_pcb_id = KONAMI_VRC4; // this allows for konami_irq to be installed at reset
+ }
+ break;
+
+ case KONAMI_VRC4:
+ if (mapper == 21 && !submapper) // Wai Wai World 2 & Ganbare Goemon Gaiden 2 (the latter with crc_hack)
+ m_cart->set_vrc_lines(crc_hack ? 7 : 2, crc_hack ? 6 : 1, 0);
+ if (mapper == 25 && !submapper) // here there is also Ganbare Goemon Gaiden which is VRC-2
+ m_cart->set_vrc_lines(crc_hack ? 2 : 0, crc_hack ? 3 : 1, 0);
+ break;
+
+ case KONAMI_VRC6:
+ if (mapper == 24)
+ m_cart->set_vrc_lines(1, 0, 0);
+ if (mapper == 26)
+ m_cart->set_vrc_lines(0, 1, 0);
+ break;
+
+ case IREM_G101:
+ if (crc_hack && !submapper)
+ m_cart->set_mirroring(PPU_MIRROR_HIGH); // Major League has hardwired mirroring
+ else if (!submapper)
+ m_cart->set_pcb_ctrl_mirror(TRUE);
+ break;
+
+ case DIS_74X161X161X32:
+ if (mapper == 70)
+ m_cart->set_mirroring(PPU_MIRROR_VERT); // only hardwired mirroring makes different mappers 70 & 152
+ else
+ m_cart->set_pcb_ctrl_mirror(TRUE);
+ break;
+
+ case SUNSOFT_2:
+ if (mapper == 93)
+ m_cart->set_mirroring(PPU_MIRROR_VERT); // only hardwired mirroring makes different mappers 89 & 93
+ else
+ m_cart->set_pcb_ctrl_mirror(TRUE);
+ break;
+
+ case HES_BOARD:
+ if (crc_hack)
+ m_cart->set_pcb_ctrl_mirror(TRUE); // Mapper 113 is used for 2 diff boards
+ break;
+
+ case CAMERICA_BF9093:
+ if (crc_hack && !submapper)
+ m_cart->set_pcb_ctrl_mirror(TRUE); // Mapper 71 is used for 2 diff boards
+ break;
+
+ case STD_BXROM:
+ if (crc_hack && !submapper)
+ m_pcb_id = AVE_NINA01; // Mapper 34 is used for 2 diff boards
+ break;
+
+ case BANDAI_LZ93:
+ if (crc_hack)
+ m_pcb_id = BANDAI_FJUMP2; // Mapper 153 is used for 2 diff boards
+ break;
+
+ case IREM_HOLYDIVR:
+ if (crc_hack && !submapper)
+ m_pcb_id = JALECO_JF16; // Mapper 78 is used for 2 diff boards
+ break;
+
+ case WAIXING_WXZS:
+ if (crc_hack)
+ m_pcb_id = WAIXING_DQ8; // Mapper 242 is used for 2 diff boards
+ break;
+
+ case BMC_GOLD_7IN1:
+ if (crc_hack)
+ m_pcb_id = BMC_MARIOPARTY_7IN1; // Mapper 52 is used for 2 diff boards
+ break;
+
+ case BTL_MARIOBABY:
+ if (crc_hack)
+ m_pcb_id = BTL_AISENSHINICOL; // Mapper 42 is used for 2 diff boards
+ break;
+
+ case TAITO_X1_017:
+ mapper_sram_size = m_cart->get_mapper_sram_size();
+ break;
+
+ case TAITO_X1_005:
+ if (mapper == 207)
+ m_cart->set_x1_005_alt(TRUE);
+ mapper_sram_size = m_cart->get_mapper_sram_size();
+ break;
+
+ case NAMCOT_163:
+ mapper_sram_size = m_cart->get_mapper_sram_size();
+ break;
+ //FIXME: we also have to fix Action 52 PRG loading somewhere...
+
+ case BANDAI_DATACH:
+ fatalerror("Bandai Datach games have to be mounted in the Datach subslot!\n");
+ break;
+ }
+
+ // Finally turn off bus conflict emulation, because the pirate variants of the boards are bus conflict free and games would glitch
+ m_cart->set_bus_conflict(bus_conflict);
+
+ // SETUP step 4: logging what we have found
+ if (!ines20)
+ {
+ logerror("Loaded game in iNES format:\n");
+ logerror("-- Mapper %d\n", mapper);
+ logerror("-- PRG 0x%x (%d x 16k chunks)\n", prg_size, prg_size / 0x4000);
+ logerror("-- VROM 0x%x (%d x 8k chunks)\n", vrom_size, vrom_size / 0x2000);
+ logerror("-- VRAM 0x%x (%d x 8k chunks)\n", vram_size, vram_size / 0x2000);
+ logerror("-- Mirroring %s\n", BIT(header[6], 0) ? "Vertical" : "Horizontal");
+ if (battery_size)
+ logerror("-- Battery found\n");
+ if (m_cart->get_trainer())
+ logerror("-- Trainer found\n");
+ if (m_cart->get_four_screen_vram())
+ logerror("-- 4-screen VRAM\n");
+ logerror("-- TV System: %s\n", ((header[10] & 3) == 0) ? "NTSC" : (header[10] & 1) ? "Both NTSC and PAL" : "PAL");
+ }
+ else
+ {
+ logerror("Loaded game in Extended iNES format:\n");
+ logerror("-- Mapper: %d\n", mapper);
+ logerror("-- Submapper: %d\n", (header[8] & 0xf0) >> 4);
+ logerror("-- PRG 0x%x (%d x 16k chunks)\n", prg_size, prg_size / 0x4000);
+ logerror("-- VROM 0x%x (%d x 8k chunks)\n", vrom_size, vrom_size / 0x2000);
+ logerror("-- VRAM 0x%x (%d x 8k chunks)\n", vram_size, vram_size / 0x2000);
+ logerror("-- PRG NVWRAM: %d\n", (header[10] & 0xf0) >> 4);
+ logerror("-- PRG WRAM: %d\n", header[10] & 0x0f);
+ logerror("-- CHR NVWRAM: %d\n", (header[11] & 0xf0) >> 4);
+ logerror("-- CHR WRAM: %d\n", header[11] & 0x0f);
+ logerror("-- TV System: %s\n", (header[12] & 2) ? "Both NTSC and PAL" : (header[12] & 1) ? "PAL" : "NTSC");
+ }
+
+ // SETUP step 5: allocate pointers for PRG/VROM
+ if (prg_size)
+ m_cart->prg_alloc(prg_size, tag());
+ if (vrom_size)
+ m_cart->vrom_alloc(vrom_size, tag());
+
+ // if there is a trainer, skip it for the moment
+ if (m_cart->get_trainer())
+ fseek(0x210, SEEK_SET);
+
+ // SETUP step 6: at last load the data!
+ // Read in the program chunks
+ if (prg16k)
+ {
+ fread(m_cart->get_prg_base(), 0x4000);
+ memcpy(m_cart->get_prg_base() + 0x4000, m_cart->get_prg_base(), 0x4000);
+ }
+ else
+ fread(m_cart->get_prg_base(), m_cart->get_prg_size());
+#if SPLIT_PRG
+ {
+ FILE *prgout;
+ char outname[255];
+
+ sprintf(outname, "%s.prg", filename());
+ prgout = fopen(outname, "wb");
+ if (prgout)
+ {
+ fwrite(m_cart->get_prg_base(), 1, 0x4000 * m_cart->get_prg_size(), prgout);
+ osd_printf_error("Created PRG chunk\n");
+ }
+
+ fclose(prgout);
+ }
+#endif
+
+ // Read in any chr chunks
+ if (m_cart->get_vrom_size())
+ fread(m_cart->get_vrom_base(), m_cart->get_vrom_size());
+
+#if SPLIT_CHR
+ if (state->m_chr_chunks > 0)
+ {
+ FILE *chrout;
+ char outname[255];
+
+ sprintf(outname, "%s.chr", filename());
+ chrout= fopen(outname, "wb");
+ if (chrout)
+ {
+ fwrite(m_cart->get_vrom_base(), 1, m_cart->get_vrom_size(), chrout);
+ osd_printf_error("Created CHR chunk\n");
+ }
+ fclose(chrout);
+ }
+#endif
+
+ // SETUP steps 7: allocate the remaining pointer, when needed
+ if (vram_size)
+ m_cart->vram_alloc(vram_size);
+ if (prgram_size || m_cart->get_trainer())
+ {
+ if (prgram_size)
+ m_cart->prgram_alloc(prgram_size);
+ else
+ m_cart->prgram_alloc(0x2000);
+ if (m_cart->get_trainer())
+ {
+ fseek(0x10, SEEK_SET);
+ fread(m_cart->get_prgram_base() + 0x1000, 0x200);
+ }
+ }
+
+
+ // Attempt to load a battery file for this ROM
+ // A few boards have internal RAM with a battery (MMC6, Taito X1-005 & X1-017, etc.)
+ if (battery_size || mapper_sram_size)
+ {
+ UINT32 tot_size = battery_size + mapper_sram_size;
+ dynamic_buffer temp_nvram(tot_size);
+ battery_load(&temp_nvram[0], tot_size, 0x00);
+ if (battery_size)
+ {
+ //printf("here %d\n", battery_size);
+ m_cart->battery_alloc(battery_size);
+ memcpy(m_cart->get_battery_base(), &temp_nvram[0], battery_size);
+ }
+ if (mapper_sram_size)
+ memcpy(m_cart->get_mapper_sram_base(), &temp_nvram[battery_size], m_cart->get_mapper_sram_size());
+ }
+}
+
+const char * nes_cart_slot_device::get_default_card_ines(UINT8 *ROM, UINT32 len)
+{
+ UINT8 mapper, submapper = 0;
+ bool ines20 = FALSE;
+ std::string mapinfo;
+ int pcb_id = 0, mapint1 = 0, mapint2 = 0, mapint3 = 0, mapint4 = 0;
+ int crc_hack = 0;
+
+ mapper = (ROM[6] & 0xf0) >> 4;
+
+ switch (ROM[7] & 0xc)
+ {
+ case 0x4:
+ case 0xc:
+ // probably the header got corrupted: don't trust upper bits for mapper
+ break;
+
+ case 0x8: // it's iNES 2.0 format
+ ines20 = TRUE;
+ case 0x0:
+ default:
+ mapper |= ROM[7] & 0xf0;
+ break;
+ }
+
+ // use info from nes.hsi if available!
+ if (hashfile_extrainfo(*this, mapinfo))
+ {
+ if (4 == sscanf(mapinfo.c_str(),"%d %d %d %d", &mapint1, &mapint2, &mapint3, &mapint4))
+ {
+ /* image is present in nes.hsi: overwrite the header settings with these */
+ mapper = mapint1;
+ crc_hack = (mapint2 & 0xf0) >> 4; // this is used to differentiate among variants of the same Mapper (see below)
+ }
+ }
+
+ // use extended iNES2.0 info if available!
+ if (ines20)
+ {
+ mapper |= (ROM[8] & 0x0f) << 8;
+ // read submappers (based on 20140116 specs)
+ submapper = (ROM[8] & 0xf0 >> 8);
+ }
+
+ ines_mapr_setup(mapper, &pcb_id);
+
+ // handle submappers
+ if (submapper)
+ {
+ // 001: MMC1
+ if (mapper == 1 && submapper == 3)
+ pcb_id = STD_SXROM_A;
+ else if (mapper == 1 && submapper == 5)
+ logerror("Unimplemented iNES2.0 submapper: SEROM/SHROM/SH1ROM.\n");
+ // 021, 023, 025: VRC4 / VRC2
+ else if (mapper == 21 || mapper == 23 || mapper == 25)
+ {
+ // 021, 023, 025: VRC4
+ int line_1 = submapper & 0x07;
+ int line_2 = (submapper & 0x08) ? line_1 + 1 : line_1 - 1;
+ if (line_2 >= 0 && line_2 <= 7)
+ pcb_id = KONAMI_VRC4;
+ else if (submapper == 15)
+ pcb_id = KONAMI_VRC2;
+ }
+ // iNES Mapper 034
+ else if (mapper == 34 && submapper == 1)
+ {
+ pcb_id = AVE_NINA01; // Mapper 34 is used for 2 diff boards
+ }
+ // iNES Mapper 078
+ else if (mapper == 78)
+ {
+ if (submapper == 1)
+ pcb_id = JALECO_JF16; // Mapper 78 is used for 2 diff boards
+ else if (submapper == 3)
+ pcb_id = IREM_HOLYDIVR;
+ }
+ }
+
+ // solve mapper conflicts
+ switch (pcb_id)
+ {
+ case STD_NROM:
+ if (ROM[4] == 3)
+ pcb_id = STD_NROM368;
+ break;
+
+ case KONAMI_VRC2:
+ if (mapper == 23 && crc_hack && !submapper)
+ pcb_id = KONAMI_VRC4; // this allows for konami_irq to be installed at reset
+ break;
+
+ case STD_BXROM:
+ if (crc_hack && !submapper)
+ pcb_id = AVE_NINA01; // Mapper 34 is used for 2 diff boards
+ break;
+
+ case BANDAI_LZ93:
+ if (crc_hack)
+ pcb_id = BANDAI_FJUMP2; // Mapper 153 is used for 2 diff boards
+ break;
+
+ case IREM_HOLYDIVR:
+ if (crc_hack && !submapper)
+ pcb_id = JALECO_JF16; // Mapper 78 is used for 2 diff boards
+ break;
+
+ case WAIXING_WXZS:
+ if (crc_hack)
+ pcb_id = WAIXING_DQ8; // Mapper 242 is used for 2 diff boards
+ break;
+
+ case BMC_GOLD_7IN1:
+ if (crc_hack)
+ pcb_id = BMC_MARIOPARTY_7IN1; // Mapper 52 is used for 2 diff boards
+ break;
+
+ case BTL_MARIOBABY:
+ if (crc_hack)
+ pcb_id = BTL_AISENSHINICOL; // Mapper 42 is used for 2 diff boards
+ break;
+ }
+
+ return nes_get_slot(pcb_id);
+}
diff --git a/src/devices/bus/nes/nes_pcb.inc b/src/devices/bus/nes/nes_pcb.inc
new file mode 100644
index 00000000000..57cdf3d498c
--- /dev/null
+++ b/src/devices/bus/nes/nes_pcb.inc
@@ -0,0 +1,609 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/*****************************************************************************************
+
+ NES Cart PCBs Emulation
+
+****************************************************************************************/
+
+struct nes_pcb
+{
+ const char *slot_opt;
+ int pcb_id;
+};
+
+// Here, we take the feature attribute from .xml (i.e. the PCB name) and we assign a unique ID to it
+static const nes_pcb pcb_list[] =
+{
+ { "nrom", STD_NROM },
+ { "hvc_basic", HVC_FAMBASIC },
+ { "nrom368", STD_NROM368 },
+ { "nrom_gg", GG_NROM },
+ { "uxrom", STD_UXROM },
+ { "unrom_cc", UXROM_CC },
+ { "cnrom", STD_CNROM },
+ { "bandai_pt554", BANDAI_PT554 },
+ { "cprom", STD_CPROM },
+ { "axrom", STD_AXROM },
+ { "pxrom", STD_PXROM },
+ { "fxrom", STD_FXROM },
+ { "bnrom", STD_BXROM },
+ { "gxrom", STD_GXROM },
+ { "un1rom", STD_UN1ROM },
+ { "sxrom", STD_SXROM },
+ { "sorom", STD_SOROM },
+ { "sxrom_a", STD_SXROM_A },
+ { "sorom_a", STD_SOROM_A },
+ { "txrom", STD_TXROM },
+ { "hkrom", STD_HKROM },
+ { "tqrom", STD_TQROM },
+ { "txsrom", STD_TXSROM },
+ { "exrom", STD_EXROM },
+ { "disksys", STD_DISKSYS },
+ { "pal_zz", PAL_ZZ },
+ { "nes_qj", NES_QJ },
+ { "nes_event", STD_EVENT },
+ { "discrete_74x139", DIS_74X139X74 },
+ { "discrete_74x377", DIS_74X377 },
+ { "discrete_74x161", DIS_74X161X161X32 },
+ { "bitcorp_dis", DIS_74X161X138 },
+ { "lz93d50", BANDAI_LZ93 },
+ { "lz93d50_ep1", BANDAI_LZ93EX1 },
+ { "lz93d50_ep2", BANDAI_LZ93EX2 },
+ { "fcg", BANDAI_FCG },
+ { "fjump2", BANDAI_FJUMP2 },
+ { "datach", BANDAI_DATACH },
+ { "karastudio", BANDAI_KARAOKE },
+ { "oekakids", BANDAI_OEKAKIDS },
+ { "g101", IREM_G101 },
+ { "lrog017", IREM_LROG017 },
+ { "h3001", IREM_H3001 },
+ { "holydivr", IREM_HOLYDIVR },
+ { "tam_s1", IREM_TAM_S1 },
+ { "jf11", JALECO_JF11 },
+ { "jf13", JALECO_JF13 },
+ { "jf16", JALECO_JF16 },
+ { "jf17", JALECO_JF17 },
+ { "jf17pcm", JALECO_JF17_ADPCM },
+ { "jf19", JALECO_JF19 },
+ { "jf19pcm", JALECO_JF19_ADPCM },
+ { "ss88006", JALECO_SS88006 },
+ { "jf23", JALECO_JF23 },
+ { "jf24", JALECO_JF24 },
+ { "jf29", JALECO_JF29 },
+ { "jf33", JALECO_JF33 },
+ { "vrc1", KONAMI_VRC1 },
+ { "vrc2", KONAMI_VRC2 },
+ { "vrc3", KONAMI_VRC3 },
+ { "vrc4", KONAMI_VRC4 },
+ { "vrc6", KONAMI_VRC6 },
+ { "vrc7", KONAMI_VRC7 },
+ { "namcot_163", NAMCOT_163 },
+ { "namcot_175", NAMCOT_175 },
+ { "namcot_340", NAMCOT_340 },
+ { "namcot_3433", NAMCOT_34X3 }, // DxROM is a Nintendo board for US versions of the 3433/3443 games
+ { "namcot_3425", NAMCOT_3425 },
+ { "namcot_3446", NAMCOT_3446 },
+ { "sunsoft1", SUNSOFT_1 },
+ { "sunsoft2", SUNSOFT_2 },
+ { "sunsoft3", SUNSOFT_3 },
+ { "sunsoft4", SUNSOFT_4 },
+ { "sunsoft_dcs", SUNSOFT_DCS },
+ { "sunsoft_fme7", SUNSOFT_FME7 }, // JxROM is a Nintendo board for US versions of the Sunsoft FME7 games
+ { "sunsoft5a", SUNSOFT_FME7 },
+ { "sunsoft5b", SUNSOFT_5 },
+ { "tc0190fmc", TAITO_TC0190FMC },
+ { "tc0190fmcp", TAITO_TC0190FMCP },
+ { "tc0350fmr", TAITO_TC0190FMC },
+ { "x1_005", TAITO_X1_005 }, // two variants exist, depending on pin17 & pin31 connections
+ { "x1_017", TAITO_X1_017 },
+ { "nina001", AVE_NINA01 },
+ { "nina006", AVE_NINA06 },
+ { "maxi15", AVE_MAXI15 },
+ { "bf9093", CAMERICA_BF9093 },
+ { "bf9096", CAMERICA_BF9096 },
+ { "goldenfive", CAMERICA_GOLDENFIVE },
+ { "ade" , CAMERICA_ALADDIN },
+ { "cne_decathl", CNE_DECATHLON },
+ { "cne_fsb", CNE_FSB },
+ { "cne_shlz", CNE_SHLZ },
+ { "nanjing", NANJING_BOARD }, // mapper 163
+ { "ntdec_asder", NTDEC_ASDER }, // mapper 112
+ { "ntdec_fh", NTDEC_FIGHTINGHERO }, // mapper 193
+ { "sa009", SACHEN_SA009 },
+ { "sa0036", SACHEN_SA0036 },
+ { "sa0037", SACHEN_SA0037 },
+ { "sa72007", SACHEN_SA72007 },
+ { "sa72008", SACHEN_SA72008 },
+ { "tca01", SACHEN_TCA01 },
+ { "s8259a", SACHEN_8259A },
+ { "s8259b", SACHEN_8259B },
+ { "s8259c", SACHEN_8259C },
+ { "s8259d", SACHEN_8259D },
+ { "s74x374", SACHEN_74LS374 },
+ { "s74x374a", SACHEN_74LS374_ALT }, /* FIXME: Made up boards some different handling */
+ { "tcu01", SACHEN_TCU01 },
+ { "tcu02", SACHEN_TCU02 },
+ { "sa9602b", SACHEN_SA9602B },
+ { "tengen_800008", TENGEN_800008 }, /* FIXME: Is this the same as mapper 3? */
+ { "tengen_800032", TENGEN_800032 },
+ { "tengen_800037", TENGEN_800037 },
+ { "txc_22211", TXC_22211 },
+ { "txc_dumarc", TXC_DUMARACING },
+ { "txc_mjblock", TXC_MJBLOCK },
+ { "txc_strikew", TXC_STRIKEW },
+ { "txc_commandos", TXC_COMMANDOS },
+ { "waixing_a", WAIXING_TYPE_A },
+ { "waixing_a1", WAIXING_TYPE_A1 }, /* FIXME: Made up boards the different CHRRAM banks (see Ji Jia Zhan Shi }, */
+ { "waixing_b", WAIXING_TYPE_B },
+ { "waixing_c", WAIXING_TYPE_C },
+ { "waixing_d", WAIXING_TYPE_D },
+ { "waixing_e", WAIXING_TYPE_E },
+ { "waixing_f", WAIXING_TYPE_F },
+ { "waixing_g", WAIXING_TYPE_G },
+ { "waixing_h", WAIXING_TYPE_H },
+ { "waixing_h1", WAIXING_TYPE_H1 },
+ { "waixing_i", WAIXING_TYPE_I },
+ { "waixing_j", WAIXING_TYPE_J },
+ { "waixing_sgz", WAIXING_SGZ },
+ { "waixing_sgzlz", WAIXING_SGZLZ },
+ { "waixing_sec", WAIXING_SECURITY },
+ { "waixing_ffv", WAIXING_FFV },
+ { "waixing_wxzs", WAIXING_WXZS },
+ { "waixing_wxzs2", WAIXING_WXZS2 },
+ { "waixing_dq8", WAIXING_DQ8 },
+ { "waixing_sh2", WAIXING_SH2 },
+ { "fs304", WAIXING_FS304 }, // used in Zelda 3 by Waixing
+ { "cony", CONY_BOARD },
+ { "yoko", YOKO_BOARD },
+ { "hengg_srich", HENGG_SRICH },
+ { "hengg_xhzs", HENGG_XHZS },
+ { "hengg_shjy3", HENGG_SHJY3 }, // mapper 253
+ { "hes", HES_BOARD },
+ { "hosenkan", HOSENKAN_BOARD },
+ { "ks7058", KAISER_KS7058 },
+ { "ks202", KAISER_KS202 }, // mapper 56
+ { "ks7022", KAISER_KS7022 }, // mapper 175
+ { "ks7017", KAISER_KS7017 },
+ { "ks7032", KAISER_KS7032 }, // mapper 142
+ { "ks7031", KAISER_KS7031 }, // used in Dracula II (FDS Conversion)
+ { "ks7012", KAISER_KS7012 }, // used in Zanac (FDS Conversion)
+ { "ks7013b", KAISER_KS7013B }, // used in Highway Star (FDS Conversion)
+ { "gs2015", RCM_GS2015 },
+ { "gs2004", RCM_GS2004 },
+ { "gs2013", RCM_GS2013 },
+ { "tf9in1", RCM_TF9IN1 },
+ { "3dblock", RCM_3DBLOCK }, // NROM + IRQ?
+ { "racermate", UNL_RACERMATE }, // mapper 168
+ { "agci_50282", AGCI_50282 },
+ { "dreamtech01", DREAMTECH_BOARD },
+ { "fukutake", FUKUTAKE_BOARD },
+ { "futuremedia", FUTUREMEDIA_BOARD },
+ { "magicseries", MAGICSERIES_MD },
+ { "daou_306", OPENCORP_DAOU306 },
+ { "subor0", SUBOR_TYPE0 },
+ { "subor1", SUBOR_TYPE1 },
+ { "cc21", UNL_CC21 },
+ { "xiaozy", UNL_XIAOZY },
+ { "edu2k", UNL_EDU2K },
+ { "t230", UNL_T230 },
+ { "mk2", UNL_MK2 },
+ // misc bootleg boards
+ { "ax5705", UNL_AX5705 },
+ { "sc127", UNL_SC127 },
+ { "mariobaby", BTL_MARIOBABY },
+ { "asnicol", BTL_AISENSHINICOL },
+ { "smb3pirate", BTL_SMB3 },
+ { "btl_dninja", BTL_DRAGONNINJA },
+ { "whirl2706", WHIRLWIND_2706 },
+ { "smb2j", UNL_SMB2J },
+ { "smb2ja", BTL_SMB2JA },
+ { "smb2jb", BTL_SMB2JB },
+ { "09034a", BTL_09034A },
+ { "tobidase", BTL_TOBIDASE }, // mapper 120
+ { "dbz5", REXSOFT_DBZ5 },
+ { "sl1632", REXSOFT_SL1632 },
+ { "somari", SOMARI_SL12 }, // mapper 116
+ { "nitra", NITRA_TDA },
+ { "ks7057", UNL_KS7057 }, // mapper 196 alt (for Street Fighter VI / Fight Street VI },
+ { "sbros11", BTL_SBROS11 },
+ { "family4646", BMC_FAMILY_4646 },
+ { "pikay2k", BTL_PIKACHUY2K }, // mapper 254
+ { "8237", UNL_8237 },
+ { "sg_lionk", SUPERGAME_LIONKING },
+ { "sg_boog", SUPERGAME_BOOGERMAN },
+ { "kasing", KASING_BOARD },
+ { "kay", KAY_BOARD },
+ { "h2288", UNL_H2288 },
+ { "unl_6035052", UNL_603_5052 }, // mapper 238?
+ { "txc_tw", TXC_TW },
+ { "kof97", UNL_KOF97 },
+ { "kof96", UNL_KOF96 },
+ { "sfight3", UNL_SF3 },
+ { "gouder", GOUDER_37017 },
+ { "benshieng", BMC_BENSHIENG },
+ { "action52", ACTENT_ACT52 },
+ { "caltron6in1", CALTRON_6IN1 },
+ { "rumblestation", RUMBLESTATION_BOARD }, // mapper 46
+ { "svision16", SVISION16_BOARD },
+ { "n625092", UNL_N625092 },
+ { "a65as", BMC_A65AS },
+ { "t262", BMC_T262 },
+ { "novel1", BMC_NOVEL1 },
+ { "novel2", BMC_NOVEL2 }, // mapper 213... same as BMC-NOVELDIAMOND9999999IN1 board?
+ { "studyngame", UNL_STUDYNGAME }, // mapper 39
+ { "sgun20in1", BMC_SUPERGUN_20IN1 },
+ { "bmc_vt5201", BMC_VT5201 }, // mapper 60 otherwise
+ { "bmc_d1038", BMC_VT5201 }, // mapper 60?
+ { "810544c", BMC_810544 },
+ { "ntd03", BMC_NTD_03 },
+ { "bmc_gb63", BMC_G63IN1 },
+ { "bmc_gka", BMC_GKA },
+ { "bmc_gkb", BMC_GKB },
+ { "bmc_ws", BMC_WS },
+ { "bmc_hik300", BMC_SUPERHIK_300IN1 },
+ { "bmc_s700", BMC_SUPER_700IN1 },
+ { "bmc_ball11", BMC_BALLGAMES_11IN1 },
+ { "bmc_22games", BMC_22GAMES },
+ { "bmc_64y2k", BMC_64IN1NR },
+ { "bmc_12in1", BMC_12IN1 },
+ { "bmc_20in1", BMC_20IN1 },
+ { "bmc_21in1", BMC_21IN1 },
+ { "bmc_31in1", BMC_31IN1 },
+ { "bmc_35in1", BMC_35IN1 },
+ { "bmc_36in1", BMC_36IN1 },
+ { "bmc_64in1", BMC_64IN1 },
+ { "bmc_70in1", BMC_70IN1 },
+ { "bmc_72in1", BMC_72IN1 },
+ { "bmc_76in1", BMC_76IN1 },
+ { "bmc_s42in1", BMC_76IN1 },
+ { "bmc_110in1", BMC_110IN1 },
+ { "bmc_150in1", BMC_150IN1 },
+ { "bmc_190in1", BMC_190IN1 },
+ { "bmc_800in1", BMC_800IN1 },
+ { "bmc_1200in1", BMC_1200IN1 },
+ { "bmc_8157", BMC_8157 },
+ { "bmc_g146", BMC_G146 },
+ { "bmc_11160", BMC_11160 },
+ { "fk23c", BMC_FK23C },
+ { "fk23ca", BMC_FK23CA },
+ { "s24in1c03", BMC_S24IN1SC03 },
+ { "bmc_15in1", BMC_15IN1 },
+ { "bmc_sbig7in1", BMC_SUPERBIG_7IN1 },
+ { "bmc_hik8in1", BMC_HIK8IN1 },
+ { "bmc_hik4in1", BMC_SUPERHIK_4IN1 },
+ { "bmc_mario7in1", BMC_MARIOPARTY_7IN1 },
+ { "bmc_gold7in1", BMC_GOLD_7IN1 },
+ { "bmc_gc6in1", BMC_GOLDENCARD_6IN1 },
+ { "bmc_411120c", BMC_411120C },
+ { "bmc_830118c", BMC_830118C },
+ { "pjoy84", BMC_PJOY84 },
+ { "bmc_gold150", BMC_GOLD150 },
+ { "bmc_gold260", BMC_GOLD260 },
+ { "bmc_power255", BMC_CH001 },
+ { "bmc_s22games", BMC_SUPER22 },
+ { "bmc_reset4", BMC_4IN1RESET },
+ { "bmc_reset42", BMC_42IN1RESET },
+ { "jyc_a", JYCOMPANY_A },
+ { "jyc_b", JYCOMPANY_B },
+ { "jyc_c", JYCOMPANY_C },
+ { "tek90", JYCOMPANY_A },
+ { "sa9602b", SACHEN_SA9602B },
+ { "unl_shero", SACHEN_SHERO },
+ { "mmalee2", UNL_MMALEE },
+ { "unl_2708", UNL_2708 },
+ { "unl_lh10", UNL_LH10 },
+ { "unl_lh32", UNL_LH32 },
+ { "unl_lh53", UNL_LH53 },
+ { "unl_ac08", UNL_AC08 },
+ { "unl_bb", UNL_BB },
+ { "unl_malisb", UNL_MALISB },
+ { "sgpipe", BTL_SHUIGUAN },
+ { "unl_whero", UNL_WORLDHERO },
+ { "unl_43272", UNL_43272 },
+ { "tf1201", UNL_TF1201 },
+ { "unl_cfight", UNL_CITYFIGHT },
+ { "nocash_nochr", NOCASH_NOCHR },
+ { "nes_action53", BTL_ACTION53 },
+ { "nes_2a03pur", BTL_2A03_PURITANS },
+ { "ffe3", FFE3_BOARD },
+ { "ffe4", FFE4_BOARD },
+ { "ffe8", FFE8_BOARD },
+ { "8237a", UNSUPPORTED_BOARD },
+ { "ninjaryu", UNSUPPORTED_BOARD },
+ { "unl_dance", UNSUPPORTED_BOARD },
+ { "bmc_hik_kof", UNSUPPORTED_BOARD },
+ { "onebus", UNSUPPORTED_BOARD },
+ { "coolboy", UNSUPPORTED_BOARD },
+ { "btl_900218", UNSUPPORTED_BOARD }, // pirate The Lord of King, to be emulated soon
+ { "a9746", UNSUPPORTED_BOARD },
+ { "dance2k", UNSUPPORTED_BOARD },
+ { "pec586", UNSUPPORTED_BOARD },
+ { "test", TEST_BOARD },
+ { "unknown", UNKNOWN_BOARD } // a few pirate dumps uses the wrong mapper...
+};
+
+static const nes_pcb *nes_pcb_lookup( const char *slot )
+{
+ for (int i = 0; i < ARRAY_LENGTH(pcb_list); i++)
+ {
+ if (!core_stricmp(pcb_list[i].slot_opt, slot))
+ return &pcb_list[i];
+ }
+ return NULL;
+}
+
+static const nes_pcb *nes_id_lookup( int id )
+{
+ for (int i = 0; i < ARRAY_LENGTH(pcb_list); i++)
+ {
+ if (pcb_list[i].pcb_id == id)
+ return &pcb_list[i];
+ }
+ return NULL;
+}
+
+int nes_cart_slot_device::nes_get_pcb_id( const char *slot )
+{
+ const nes_pcb *pcb = nes_pcb_lookup(slot);
+
+ if (pcb == NULL)
+ fatalerror("Unimplemented PCB type %s\n", slot);
+
+ return pcb->pcb_id;
+}
+
+
+const char * nes_cart_slot_device::nes_get_slot( int pcb_id )
+{
+ const nes_pcb *pcb = nes_id_lookup(pcb_id);
+
+ if (pcb == NULL)
+ fatalerror("Unimplemented PCB ID %d\n", pcb_id);
+
+ return pcb->slot_opt;
+}
+
+
+/*************************************************************
+
+ nes_pcb_reset
+
+ Resets the mmc bankswitch areas to their defaults.
+ It returns a value "err" that indicates if it was
+ successful. Possible values for err are:
+
+ 0 = success
+ 1 = no pcb found
+ 2 = pcb not supported
+
+ *************************************************************/
+
+struct nes_cart_lines
+{
+ const char *tag;
+ int line;
+};
+
+static const struct nes_cart_lines nes_cart_lines_table[] =
+{
+ { "PRG A0", 0 },
+ { "PRG A1", 1 },
+ { "PRG A2", 2 },
+ { "PRG A3", 3 },
+ { "PRG A4", 4 },
+ { "PRG A5", 5 },
+ { "PRG A6", 6 },
+ { "PRG A7", 7 },
+ { "CHR A10", 10 },
+ { "CHR A11", 11 },
+ { "CHR A12", 12 },
+ { "CHR A13", 13 },
+ { "CHR A14", 14 },
+ { "CHR A15", 15 },
+ { "CHR A16", 16 },
+ { "CHR A17", 17 },
+ { "NC", 127 },
+ { 0 }
+};
+
+static int nes_cart_get_line( const char *feature )
+{
+ const struct nes_cart_lines *nes_line = &nes_cart_lines_table[0];
+
+ if (feature == NULL)
+ return 128;
+
+ while (nes_line->tag)
+ {
+ if (strcmp(nes_line->tag, feature) == 0)
+ break;
+
+ nes_line++;
+ }
+
+ return nes_line->line;
+}
+
+void nes_cart_slot_device::call_load_pcb()
+{
+ UINT32 vram_size = 0, prgram_size = 0, battery_size = 0, mapper_sram_size = 0;
+ // SETUP step 1: getting PRG, VROM, VRAM sizes
+ UINT32 prg_size = get_software_region_length("prg");
+ UINT32 vrom_size = get_software_region_length("chr");
+ vram_size = get_software_region_length("vram");
+ vram_size += get_software_region_length("vram2");
+
+ // validate the xml fields
+ if (!prg_size)
+ fatalerror("No PRG entry for this software! Please check if the xml list got corrupted\n");
+ if (prg_size < 0x8000)
+ fatalerror("PRG entry is too small! Please check if the xml list got corrupted\n");
+
+ // SETUP step 2: getting PCB and other settings
+ if (get_feature("slot"))
+ m_pcb_id = nes_get_pcb_id(get_feature("slot"));
+ else
+ m_pcb_id = NO_BOARD;
+
+ // SETUP step 3: storing the info needed for emulation
+ if (get_software_region("bwram") != NULL)
+ battery_size = get_software_region_length("bwram");
+
+ if (get_software_region("wram") != NULL)
+ prgram_size = get_software_region_length("wram");
+
+ if (get_feature("mirroring"))
+ {
+ const char *mirroring = get_feature("mirroring");
+ if (!strcmp(mirroring, "horizontal"))
+ m_cart->set_mirroring(PPU_MIRROR_HORZ);
+ if (!strcmp(mirroring, "vertical"))
+ m_cart->set_mirroring(PPU_MIRROR_VERT);
+ if (!strcmp(mirroring, "high"))
+ m_cart->set_mirroring(PPU_MIRROR_HIGH);
+ if (!strcmp(mirroring, "low"))
+ m_cart->set_mirroring(PPU_MIRROR_LOW);
+ if (!strcmp(mirroring, "4screen"))
+ {
+ // A few boards uses 4-screen mirroring: Gauntlet (DDROM or TRR1ROM or Tengen 800004),
+ // Rad Racer II (TVROM), and Napoleon Senki (IREM LROG017 with 74*161/161/21/138)
+ m_cart->set_four_screen_vram(TRUE);
+ m_cart->set_mirroring(PPU_MIRROR_4SCREEN);
+ }
+ if (!strcmp(mirroring, "pcb_controlled"))
+ {
+ // A few boards have variants with hardcoded mirroring and variants with mapper
+ // controlled mirroring. We use a variable to avoid the need of dupe devices.
+ // See e.g. HES 6-in-1 vs other HES games, Irem Major League vs other G-101 games,
+ // Sunsoft-2 Shanghai vs Mito Koumon, Camerica BF9093 games vs BF9097 games, etc.
+ // Boards where all games control mirroring do not make real use of this.
+ m_cart->set_pcb_ctrl_mirror(TRUE);
+ }
+ }
+
+ /* Check for pins in specific boards which require them */
+ if (m_pcb_id == STD_CNROM)
+ {
+ int mask = 0, state = 0;
+
+ if (get_feature("chr-pin26") != NULL)
+ {
+ mask |= 0x01;
+ state |= !strcmp(get_feature("chr-pin26"), "CE") ? 0x01 : 0;
+ }
+ if (get_feature("chr-pin27") != NULL)
+ {
+ mask |= 0x02;
+ state |= !strcmp(get_feature("chr-pin27"), "CE") ? 0x02 : 0;
+ }
+
+ m_cart->set_ce(mask, state);
+ }
+
+ if (m_pcb_id == TAITO_X1_005 && get_feature("x1-pin17") != NULL && get_feature("x1-pin31") != NULL)
+ {
+ if (!strcmp(get_feature("x1-pin17"), "CIRAM A10") && !strcmp(get_feature("x1-pin31"), "NC"))
+ m_cart->set_x1_005_alt(TRUE);
+ }
+
+ if (m_pcb_id == KONAMI_VRC2)
+ {
+ m_cart->set_vrc_lines(nes_cart_get_line(get_feature("vrc2-pin3")),
+ nes_cart_get_line(get_feature("vrc2-pin4")),
+ (nes_cart_get_line(get_feature("vrc2-pin21")) != 10) ? 1 : 0);
+// osd_printf_error("VRC-2, pin3: A%d, pin4: A%d, pin21: %d\n", nes_cart_get_line(get_feature("vrc2-pin3")), nes_cart_get_line(get_feature("vrc2-pin4")),
+// nes_cart_get_line(get_feature("vrc2-pin21")));
+ }
+
+ if (m_pcb_id == KONAMI_VRC4)
+ {
+ m_cart->set_vrc_lines(nes_cart_get_line(get_feature("vrc4-pin3")),
+ nes_cart_get_line(get_feature("vrc4-pin4")),
+ 0);
+// osd_printf_error("VRC-4, pin3: A%d, pin4: A%d\n", nes_cart_get_line(get_feature("vrc4-pin3"), nes_cart_get_line(get_feature("vrc4-pin4"));
+ }
+
+ if (m_pcb_id == KONAMI_VRC6)
+ {
+ m_cart->set_vrc_lines(nes_cart_get_line(get_feature("vrc6-pin9")),
+ nes_cart_get_line(get_feature("vrc6-pin10")),
+ 0);
+// osd_printf_error("VRC-6, pin9: A%d, pin10: A%d\n", nes_cart_get_line(get_feature("vrc6-pin9"), nes_cart_get_line(get_feature("vrc6-pin10"));
+ }
+
+ if (m_pcb_id == STD_HKROM || m_pcb_id == TAITO_X1_017)
+ mapper_sram_size = m_cart->get_mapper_sram_size();
+
+ if (m_pcb_id == TAITO_X1_005 || m_pcb_id == NAMCOT_163)
+ {
+ if (get_feature("batt"))
+ mapper_sram_size = m_cart->get_mapper_sram_size();
+ }
+
+
+ // pirate variants of boards with bus conflict are often not suffering from it
+ // and actually games glitch if bus conflict is emulated...
+ if (get_feature("bus_conflict") && !strcmp(get_feature("bus_conflict"), "no"))
+ m_cart->set_bus_conflict(FALSE);
+
+
+ // SETUP step 4: logging what we have found
+ logerror("Loaded game from softlist:\n");
+ logerror("-- PCB: %s", get_feature("pcb"));
+ if (m_pcb_id == UNSUPPORTED_BOARD)
+ logerror(" (currently not supported by MESS)");
+ logerror("\n-- PRG 0x%x (%d x 16k chunks)\n", prg_size, prg_size / 0x4000);
+ logerror("-- VROM 0x%x (%d x 8k chunks)\n", vrom_size, vrom_size / 0x2000);
+ logerror("-- VRAM 0x%x (%d x 8k chunks)\n", vram_size, vram_size / 0x2000);
+ logerror("-- PRG NVWRAM: %d\n", battery_size + mapper_sram_size);
+ logerror("-- PRG WRAM: %d\n", prgram_size);
+
+ // SETUP steps 5/6: allocate pointers for PRG/VROM and load the data!
+ m_cart->prg_alloc(prg_size, tag());
+ memcpy(m_cart->get_prg_base(), get_software_region("prg"), prg_size);
+ if (vrom_size)
+ {
+ m_cart->vrom_alloc(vrom_size, tag());
+ memcpy(m_cart->get_vrom_base(), get_software_region("chr"), vrom_size);
+ }
+
+ // SETUP steps 7: allocate the remaining pointer, when needed
+ if (vram_size)
+ m_cart->vram_alloc(vram_size);
+ if (prgram_size)
+ m_cart->prgram_alloc(prgram_size);
+
+ // also nes_smb2j_device needs WRAM initialized to 0xff? check!
+ if (m_pcb_id == UNL_SMB2J)
+ memset(m_cart->get_prgram_base(), 0xff, prgram_size);
+
+ // Attempt to load a battery file for this ROM
+ // A few boards have internal RAM with a battery (MMC6, Taito X1-005 & X1-017, etc.)
+ if (battery_size || mapper_sram_size)
+ {
+ UINT32 tot_size = battery_size + mapper_sram_size;
+ dynamic_buffer temp_nvram(tot_size);
+
+ // some games relies on specific battery patterns to work
+ // (e.g. Silva Saga does not work with SRAM fully initialized to 0x00)
+ // and we use the info from xml here to prepare a default NVRAM
+ dynamic_buffer default_nvram(tot_size);
+ if (battery_size)
+ memcpy(&default_nvram[0], get_software_region("bwram"), battery_size);
+ if (mapper_sram_size)
+ memset(&default_nvram[battery_size], 0, mapper_sram_size);
+
+ // load battery (using default if no battery exists)
+ battery_load(&temp_nvram[0], tot_size, &default_nvram[0]);
+
+ // copy battery into PCB arrays
+ if (battery_size)
+ {
+ m_cart->battery_alloc(battery_size);
+ memcpy(m_cart->get_battery_base(), &temp_nvram[0], battery_size);
+ }
+ if (mapper_sram_size)
+ memcpy(m_cart->get_mapper_sram_base(), &temp_nvram[battery_size], mapper_sram_size);
+ }
+}
diff --git a/src/devices/bus/nes/nes_slot.c b/src/devices/bus/nes/nes_slot.c
new file mode 100644
index 00000000000..ec1b1f5f4af
--- /dev/null
+++ b/src/devices/bus/nes/nes_slot.c
@@ -0,0 +1,1053 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ Nintendo NES/FC cart emulation
+ (through slot devices)
+
+ The driver exposes address ranges
+ 0x4100-0x5fff to read_l/write_l
+ 0x6000-0x7fff to read_m/write_m (here are *usually* installed NVRAM & WRAM, if any)
+ 0x8000-0xffff to write_h (reads are directed to 4 x 8K PRG banks)
+ Default implementations of these handlers are available here, to be rewritten by PCB-specific ones when needed.
+
+ Additional handlers are available, but have to be manually installed at machine_start
+ * read_ex/write_ex for address range 0x4020-0x40ff
+ * read_h for address range 0x8000-0xffff when a cart does some protection or address scramble before reading ROM
+
+ PPU exposes address ranges
+ 0x0000-0x1fff to chr_r/chr_w
+ 0x2000-0x3eff to nt_r/nt_w
+ Default implementations of these handlers are available here, to be rewritten by PCB-specific ones when needed.
+
+ Plus a few of latch functions are available: ppu_latch (see MMC2), hblank_irq and scanline_irq (see e.g. MMC3),
+ but these might be subject to future changes when the PPU is revisited.
+
+ Notes:
+ - Differently from later systems (like SNES or MD), it is uncommon to find PRG ROMs of NES games which are not a
+ power of 2K, so we do not perform any mirroring by default.
+ A bunch of pcb types, though, come with 1.5MB of PRG (some Waixing translations) or with multiple PRG chips
+ having peculiar size (32K + 16K, 32K + 8K, 32K + 2K). Hence, if such a configuration is detected, we provide
+ a m_prg_bank_map array to handle internally PRG mirroring up to the next power of 2K, as long as the size is
+ a multiple of 8K (i.e. the unit chunk for standard PRG).
+ For the case of PRG chips which are not-multiple of 8K (e.g. UNL-MARIO2-MALEE pcb), the handling has to be
+ handled in the pcb-specific code!
+ - Our open bus emulation is very sketchy, by simply returning the higher 8bits of the accessed address. This seems
+ enough for most games (only two sets have issues with this). A slightly better implementation is almost ready
+ to fix these two remaining cases, but I plan to revisit the whole implementation in an accurate way at a later
+ stage
+ - Bus conflict is implemented based on latest tests by Blargg. There is some uncertainty about AxROM behavior
+ (some AOROM pcbs suffers from bus conflict, some do not... since no AOROM game is known to glitch due to lack
+ of bus conflict it seems safe to emulate the board without bus conflict, but eventually it would be good to
+ differentiate the real variants)
+
+
+ Many information about the mappers/pcbs come from the wonderful doc written by Disch.
+ Current info (when used) are based on v0.6.1 of his docs.
+ You can find the latest version of the doc at http://www.romhacking.net/docs/362/
+
+ A lot of details have been based on the researches carried on at NesDev forums (by Blargg, Quietust and many more)
+ and collected on the NesDev Wiki http://wiki.nesdev.com/
+
+ Particular thanks go to
+ - Martin Freij for his work on NEStopia
+ - Cah4e3 for his efforts on FCEUMM and the reverse engineering of pirate boards
+ - BootGod, lidnariq and naruko for the PCB tests which made possible
+
+
+ ***********************************************************************************************************/
+
+/*****************************************************************************************
+
+ A few Mappers suffer of hardware conflict: original dumpers have used the same mapper number for more than
+ a kind of boards. In these cases (and only in these cases) we exploit nes.hsi to set up accordingly
+ emulation. Games which requires this hack are the following:
+ * 032 - Major League needs hardwired mirroring (missing windows and glitched field in top view, otherwise)
+ * 034 - Impossible Mission II is not like BxROM games (it writes to 0x7ffd-0x7fff, instead of 0x8000). It is still
+ unplayable though (see above)
+ * 071 - Fire Hawk is different from other Camerica games (no hardwired mirroring). Without crc_hack no helicopter graphics
+ * 078 - Cosmo Carrier needs a different mirroring than Holy Diver
+ * 113 - HES 6-in-1 requires mirroring (check Bookyman playfield), while other games break with this (check AV Soccer)
+ * 153 - Famicom Jump II uses a different board (or the same in a very different way)
+ * 242 - DQ8 has no mirroring (missing graphics is due to other reasons though)
+
+ crc_hacks have been added also to handle a few wiring settings which would require submappers:
+ * CHR protection pins for mapper 185
+ * VRC-2, VRC-4 and VRC-6 line wiring
+
+ Remember that the MMC # does not equal the mapper #. In particular, Mapper 4 is
+ in fact MMC3, Mapper 9 is MMC2 and Mapper 10 is MMC4. Makes perfect sense, right?
+
+ ****************************************************************************************/
+
+
+#include "emu.h"
+#include "hashfile.h"
+#include "nes_slot.h"
+
+#define NES_BATTERY_SIZE 0x2000
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+const device_type NES_CART_SLOT = &device_creator<nes_cart_slot_device>;
+
+
+//**************************************************************************
+// NES cartridges Interface
+//**************************************************************************
+
+//-------------------------------------------------
+// device_nes_cart_interface - constructor
+//-------------------------------------------------
+
+device_nes_cart_interface::device_nes_cart_interface(const machine_config &mconfig, device_t &device)
+ : device_slot_card_interface(mconfig, device),
+ m_prg(NULL),
+ m_vrom(NULL),
+ m_ciram(NULL),
+ m_prg_size(0),
+ m_vrom_size(0),
+ m_mapper_sram(NULL),
+ m_mapper_sram_size(0),
+ m_ce_mask(0),
+ m_ce_state(0),
+ m_vrc_ls_prg_a(0),
+ m_vrc_ls_prg_b(0),
+ m_vrc_ls_chr(0),
+ m_mirroring(PPU_MIRROR_NONE),
+ m_pcb_ctrl_mirror(FALSE),
+ m_four_screen_vram(FALSE),
+ m_has_trainer(FALSE),
+ m_x1_005_alt_mirroring(FALSE),
+ m_bus_conflict(TRUE),
+ m_open_bus(0),
+ m_prg_chunks(0),
+ m_prg_mask(0xffff),
+ m_chr_source(CHRRAM),
+ m_vrom_chunks(0),
+ m_vram_chunks(0)
+{
+}
+
+
+//-------------------------------------------------
+// ~device_nes_cart_interface - destructor
+//-------------------------------------------------
+
+device_nes_cart_interface::~device_nes_cart_interface()
+{
+}
+
+//-------------------------------------------------
+// pointer allocators
+//-------------------------------------------------
+
+void device_nes_cart_interface::prg_alloc(size_t size, const char *tag)
+{
+ if (m_prg == NULL)
+ {
+ m_prg = device().machine().memory().region_alloc(std::string(tag).append(NESSLOT_PRGROM_REGION_TAG).c_str(), size, 1, ENDIANNESS_LITTLE)->base();
+ m_prg_size = size;
+ m_prg_chunks = size / 0x4000;
+ if (size % 0x2000)
+ {
+ // A few pirate carts have PRG made of 32K + 2K or some weird similar config
+ // in this case we treat the banking as if this 'extra' PRG is not present and
+ // the pcb code has to handle it by accessing directly m_prg!
+ printf("Warning! The loaded PRG has size not a multiple of 8KB (0x%X)\n", (UINT32)size);
+ m_prg_chunks--;
+ }
+
+ m_prg_mask = ((m_prg_chunks << 1) - 1);
+
+// printf("first mask %x!\n", m_prg_mask);
+ if ((m_prg_chunks << 1) & m_prg_mask)
+ {
+ int mask_bits = 0, temp = (m_prg_chunks << 1), mapsize;
+ // contrary to what happens with later systems, like e.g. SNES or MD,
+ // only half a dozen of NES carts have PRG which is not a power of 2
+ // so we use this bank_map only as an exception
+// printf("uneven rom!\n");
+
+ // 1. redefine mask as (next power of 2)-1
+ for (; temp; )
+ {
+ mask_bits++;
+ temp >>= 1;
+ }
+ m_prg_mask = (1 << mask_bits) - 1;
+// printf("new mask %x!\n", m_prg_mask);
+ mapsize = (1 << mask_bits)/2;
+
+ // 2. create a bank_map for banks in the range mask/2 -> mask
+ m_prg_bank_map.resize(mapsize);
+
+ // 3. fill the bank_map accounting for mirrors
+ int j;
+ for (j = mapsize; j < (m_prg_chunks << 1); j++)
+ m_prg_bank_map[j - mapsize] = j;
+
+ while (j % mapsize)
+ {
+ int k = 0, repeat_banks;
+ while ((j % (mapsize >> k)) && k < mask_bits)
+ k++;
+ repeat_banks = j % (mapsize >> (k - 1));
+ for (int l = 0; l < repeat_banks; l++)
+ m_prg_bank_map[(j - mapsize) + l] = m_prg_bank_map[(j - mapsize) + l - repeat_banks];
+ j += repeat_banks;
+ }
+
+ // check bank map!
+// for (int i = 0; i < mapsize; i++)
+// {
+// printf("bank %3d = %3d\t", i, m_prg_bank_map[i]);
+// if ((i%8) == 7)
+// printf("\n");
+// }
+ }
+ }
+}
+
+void device_nes_cart_interface::vrom_alloc(size_t size, const char *tag)
+{
+ if (m_vrom == NULL)
+ {
+ std::string tempstring(tag);
+ tempstring.append(NESSLOT_CHRROM_REGION_TAG);
+ m_vrom = device().machine().memory().region_alloc(tempstring.c_str(), size, 1, ENDIANNESS_LITTLE)->base();
+ m_vrom_size = size;
+ m_vrom_chunks = size / 0x2000;
+ }
+}
+
+void device_nes_cart_interface::prgram_alloc(size_t size)
+{
+ m_prgram.resize(size);
+}
+
+void device_nes_cart_interface::vram_alloc(size_t size)
+{
+ m_vram.resize(size);
+ m_vram_chunks = size / 0x2000;
+}
+
+void device_nes_cart_interface::battery_alloc(size_t size)
+{
+ m_battery.resize(size);
+}
+
+
+//-------------------------------------------------
+// PRG helpers
+//-------------------------------------------------
+
+inline int device_nes_cart_interface::prg_8k_bank_num(int bank_8k)
+{
+ if (m_prg_mask == ((m_prg_chunks << 1) - 1))
+ return bank_8k & m_prg_mask;
+
+ // only a few pirate games (less than a dozen) have PRG which is not power of 2
+ // so we treat it here separately, rather than forcing all games to use m_prg_bank_map
+
+ // case 1: if we are accessing a bank before the end of the image, just return that bank
+ if (bank_8k < ((m_prg_chunks << 1) - 1))
+ return bank_8k;
+
+ // case 2: otherwise return a mirror using the bank_map!
+// UINT8 temp = bank_8k;
+ bank_8k &= m_prg_mask;
+ bank_8k -= (m_prg_mask/2 + 1);
+// printf("bank: accessed %x (top: %x), returned %x\n", temp, (m_prg_chunks << 1) - 1, m_prg_bank_map[bank_8k]);
+ return m_prg_bank_map[bank_8k];
+}
+
+inline void device_nes_cart_interface::update_prg_banks(int prg_bank_start, int prg_bank_end)
+{
+ for (int prg_bank = prg_bank_start; prg_bank <= prg_bank_end; prg_bank++)
+ {
+ assert(prg_bank >= 0);
+ assert(prg_bank < ARRAY_LENGTH(m_prg_bank));
+ assert(prg_bank < ARRAY_LENGTH(m_prg_bank_mem));
+
+ m_prg_bank_mem[prg_bank]->set_entry(m_prg_bank[prg_bank]);
+ }
+}
+
+void device_nes_cart_interface::prg32(int bank)
+{
+ /* if there is only 16k PRG, return */
+ if (!(m_prg_chunks >> 1))
+ return;
+
+ /* assumes that bank references a 32k chunk */
+ bank = prg_8k_bank_num(bank * 4);
+
+ m_prg_bank[0] = bank + 0;
+ m_prg_bank[1] = bank + 1;
+ m_prg_bank[2] = bank + 2;
+ m_prg_bank[3] = bank + 3;
+ update_prg_banks(0, 3);
+}
+
+void device_nes_cart_interface::prg16_89ab(int bank)
+{
+ /* assumes that bank references a 16k chunk */
+ bank = prg_8k_bank_num(bank * 2);
+
+ m_prg_bank[0] = bank + 0;
+ m_prg_bank[1] = bank + 1;
+ update_prg_banks(0, 1);
+}
+
+void device_nes_cart_interface::prg16_cdef(int bank)
+{
+ /* assumes that bank references a 16k chunk */
+ bank = prg_8k_bank_num(bank * 2);
+
+ m_prg_bank[2] = bank + 0;
+ m_prg_bank[3] = bank + 1;
+ update_prg_banks(2, 3);
+}
+
+void device_nes_cart_interface::prg8_89(int bank)
+{
+ /* assumes that bank references an 8k chunk */
+ bank = prg_8k_bank_num(bank);
+
+ m_prg_bank[0] = bank;
+ update_prg_banks(0, 0);
+}
+
+void device_nes_cart_interface::prg8_ab(int bank)
+{
+ /* assumes that bank references an 8k chunk */
+ bank = prg_8k_bank_num(bank);
+
+ m_prg_bank[1] = bank;
+ update_prg_banks(1, 1);
+}
+
+void device_nes_cart_interface::prg8_cd(int bank)
+{
+ /* assumes that bank references an 8k chunk */
+ bank = prg_8k_bank_num(bank);
+
+ m_prg_bank[2] = bank;
+ update_prg_banks(2, 2);
+}
+
+void device_nes_cart_interface::prg8_ef(int bank)
+{
+ /* assumes that bank references an 8k chunk */
+ bank = prg_8k_bank_num(bank);
+
+ m_prg_bank[3] = bank;
+ update_prg_banks(3, 3);
+}
+
+/* We also define an additional helper to map 8k PRG-ROM to one of the banks (passed as parameter) */
+void device_nes_cart_interface::prg8_x(int start, int bank)
+{
+ assert(start < 4);
+
+ /* assumes that bank references an 8k chunk */
+ bank = prg_8k_bank_num(bank);
+
+ m_prg_bank[start] = bank;
+ update_prg_banks(start, start);
+}
+
+//-------------------------------------------------
+// CHR helpers
+//-------------------------------------------------
+
+inline void device_nes_cart_interface::chr_sanity_check( int source )
+{
+ if (source == CHRRAM && m_vram.empty())
+ fatalerror("CHRRAM bankswitch with no VRAM\n");
+
+ if (source == CHRROM && m_vrom == NULL)
+ fatalerror("CHRROM bankswitch with no VROM\n");
+}
+
+void device_nes_cart_interface::chr8(int bank, int source)
+{
+ chr_sanity_check(source);
+
+ if (source == CHRRAM)
+ {
+ bank &= (m_vram_chunks - 1);
+ for (int i = 0; i < 8; i++)
+ {
+ m_chr_src[i] = source;
+ m_chr_orig[i] = (bank * 0x2000) + (i * 0x400); // for save state uses!
+ m_chr_access[i] = &m_vram[m_chr_orig[i]];
+ }
+ }
+ else
+ {
+ bank &= (m_vrom_chunks - 1);
+ for (int i = 0; i < 8; i++)
+ {
+ m_chr_src[i] = source;
+ m_chr_orig[i] = (bank * 0x2000) + (i * 0x400); // for save state uses!
+ m_chr_access[i] = &m_vrom[m_chr_orig[i]];
+ }
+ }
+}
+
+void device_nes_cart_interface::chr4_x(int start, int bank, int source)
+{
+ chr_sanity_check(source);
+
+ if (source == CHRRAM)
+ {
+ bank &= ((m_vram_chunks << 1) - 1);
+ for (int i = 0; i < 4; i++)
+ {
+ m_chr_src[i + start] = source;
+ m_chr_orig[i + start] = (bank * 0x1000) + (i * 0x400); // for save state uses!
+ m_chr_access[i + start] = &m_vram[m_chr_orig[i + start]];
+ }
+ }
+ else
+ {
+ bank &= ((m_vrom_chunks << 1) - 1);
+ for (int i = 0; i < 4; i++)
+ {
+ m_chr_src[i + start] = source;
+ m_chr_orig[i + start] = (bank * 0x1000) + (i * 0x400); // for save state uses!
+ m_chr_access[i + start] = &m_vrom[m_chr_orig[i + start]];
+ }
+ }
+}
+
+void device_nes_cart_interface::chr2_x(int start, int bank, int source)
+{
+ chr_sanity_check(source);
+
+ if (source == CHRRAM)
+ {
+ bank &= ((m_vram_chunks << 2) - 1);
+ for (int i = 0; i < 2; i++)
+ {
+ m_chr_src[i + start] = source;
+ m_chr_orig[i + start] = (bank * 0x800) + (i * 0x400); // for save state uses!
+ m_chr_access[i + start] = &m_vram[m_chr_orig[i + start]];
+ }
+ }
+ else
+ {
+ bank &= ((m_vrom_chunks << 2) - 1);
+ for (int i = 0; i < 2; i++)
+ {
+ m_chr_src[i + start] = source;
+ m_chr_orig[i + start] = (bank * 0x800) + (i * 0x400); // for save state uses!
+ m_chr_access[i + start] = &m_vrom[m_chr_orig[i + start]];
+ }
+ }
+}
+
+void device_nes_cart_interface::chr1_x(int start, int bank, int source)
+{
+ chr_sanity_check(source);
+
+ if (source == CHRRAM)
+ {
+ bank &= ((m_vram_chunks << 3) - 1);
+ m_chr_src[start] = source;
+ m_chr_orig[start] = (bank * 0x400); // for save state uses!
+ m_chr_access[start] = &m_vram[m_chr_orig[start]];
+ }
+ else
+ {
+ bank &= ((m_vrom_chunks << 3) - 1);
+ m_chr_src[start] = source;
+ m_chr_orig[start] = (bank * 0x400); // for save state uses!
+ m_chr_access[start] = &m_vrom[m_chr_orig[start]];
+ }
+}
+
+//-------------------------------------------------
+// NT & Mirroring helpers
+//-------------------------------------------------
+
+void device_nes_cart_interface::set_nt_page(int page, int source, int bank, int writable)
+{
+ UINT8* base_ptr;
+
+ switch (source)
+ {
+ case CART_NTRAM:
+ base_ptr = &m_ext_ntram[0];
+ break;
+ case VROM:
+ bank &= ((m_vrom_chunks << 3) - 1);
+ base_ptr = m_vrom;
+ break;
+ case EXRAM:
+ case MMC5FILL:
+ base_ptr = NULL;
+ break;
+ case CIRAM:
+ default:
+ base_ptr = m_ciram;
+ break;
+ }
+
+ page &= 3; /* mask down to the 4 logical pages */
+ m_nt_src[page] = source;
+
+ if (base_ptr)
+ {
+ m_nt_orig[page] = bank * 0x400;
+ m_nt_access[page] = base_ptr + m_nt_orig[page];
+ }
+
+ m_nt_writable[page] = writable;
+}
+
+void device_nes_cart_interface::set_nt_mirroring(int mirroring)
+{
+ /* setup our videomem handlers based on mirroring */
+ switch (mirroring)
+ {
+ case PPU_MIRROR_VERT:
+ set_nt_page(0, CIRAM, 0, 1);
+ set_nt_page(1, CIRAM, 1, 1);
+ set_nt_page(2, CIRAM, 0, 1);
+ set_nt_page(3, CIRAM, 1, 1);
+ break;
+
+ case PPU_MIRROR_HORZ:
+ set_nt_page(0, CIRAM, 0, 1);
+ set_nt_page(1, CIRAM, 0, 1);
+ set_nt_page(2, CIRAM, 1, 1);
+ set_nt_page(3, CIRAM, 1, 1);
+ break;
+
+ case PPU_MIRROR_HIGH:
+ set_nt_page(0, CIRAM, 1, 1);
+ set_nt_page(1, CIRAM, 1, 1);
+ set_nt_page(2, CIRAM, 1, 1);
+ set_nt_page(3, CIRAM, 1, 1);
+ break;
+
+ case PPU_MIRROR_LOW:
+ set_nt_page(0, CIRAM, 0, 1);
+ set_nt_page(1, CIRAM, 0, 1);
+ set_nt_page(2, CIRAM, 0, 1);
+ set_nt_page(3, CIRAM, 0, 1);
+ break;
+
+ case PPU_MIRROR_4SCREEN:
+ if (m_ext_ntram.empty()) fatalerror("4-screen mirroring without on-cart NTRAM!\n");
+ set_nt_page(0, CART_NTRAM, 0, 1);
+ set_nt_page(1, CART_NTRAM, 1, 1);
+ set_nt_page(2, CART_NTRAM, 2, 1);
+ set_nt_page(3, CART_NTRAM, 3, 1);
+ break;
+
+ case PPU_MIRROR_NONE:
+ default:
+ set_nt_page(0, CIRAM, 0, 1);
+ set_nt_page(1, CIRAM, 0, 1);
+ set_nt_page(2, CIRAM, 1, 1);
+ set_nt_page(3, CIRAM, 1, 1);
+ break;
+ }
+}
+
+//-------------------------------------------------
+// Other helpers
+//-------------------------------------------------
+
+// Helper function for the few mappers reading from 0x8000-0xffff for protection
+// so that they can access the ROM after the protection handling (which overwrites
+// the memory banks)
+UINT8 device_nes_cart_interface::hi_access_rom(UINT32 offset)
+{
+ int bank = (offset & 0x6000) >> 13;
+ return m_prg[m_prg_bank[bank] * 0x2000 + (offset & 0x1fff)];
+}
+
+// Helper function for the few mappers subject to bus conflict at write.
+// Tests by blargg showed that in many of the boards suffering of CPU/ROM
+// conflicts the behaviour can be accurately emulated by writing not the
+// original data, but data & rom[offset]
+UINT8 device_nes_cart_interface::account_bus_conflict(UINT32 offset, UINT8 data)
+{
+ // pirate variants of boards subject to bus conflict are often not subject to it
+ // so we allow to set m_bus_conflict to FALSE at loading time when necessary
+ if (m_bus_conflict)
+ return data & hi_access_rom(offset);
+ else
+ return data;
+}
+
+
+//-------------------------------------------------
+// PPU accessors
+//-------------------------------------------------
+
+WRITE8_MEMBER(device_nes_cart_interface::chr_w)
+{
+ int bank = offset >> 10;
+
+ if (m_chr_src[bank] == CHRRAM)
+ m_chr_access[bank][offset & 0x3ff] = data;
+}
+
+READ8_MEMBER(device_nes_cart_interface::chr_r)
+{
+ int bank = offset >> 10;
+ return m_chr_access[bank][offset & 0x3ff];
+}
+
+
+WRITE8_MEMBER(device_nes_cart_interface::nt_w)
+{
+ int page = ((offset & 0xc00) >> 10);
+
+ if (!m_nt_writable[page])
+ return;
+
+ m_nt_access[page][offset & 0x3ff] = data;
+}
+
+READ8_MEMBER(device_nes_cart_interface::nt_r)
+{
+ int page = ((offset & 0xc00) >> 10);
+ return m_nt_access[page][offset & 0x3ff];
+}
+
+
+//-------------------------------------------------
+// Base memory accessors (emulating open bus
+// behaviour and/or WRAM accesses)
+// Open bus emulation is defective, but it should
+// be enough for the few cases known to rely on
+// this (more in the comments at the top of the
+// source)
+//-------------------------------------------------
+
+READ8_MEMBER(device_nes_cart_interface::read_l)
+{
+ return m_open_bus;
+}
+
+READ8_MEMBER(device_nes_cart_interface::read_m)
+{
+ if (!m_battery.empty())
+ return m_battery[offset & (m_battery.size() - 1)];
+ if (!m_prgram.empty())
+ return m_prgram[offset & (m_prgram.size() - 1)];
+
+ return m_open_bus;
+}
+
+WRITE8_MEMBER(device_nes_cart_interface::write_l)
+{
+}
+
+WRITE8_MEMBER(device_nes_cart_interface::write_m)
+{
+ if (!m_battery.empty())
+ m_battery[offset & (m_battery.size() - 1)] = data;
+ if (!m_prgram.empty())
+ m_prgram[offset & (m_prgram.size() - 1)] = data;
+}
+
+WRITE8_MEMBER(device_nes_cart_interface::write_h)
+{
+}
+
+
+void device_nes_cart_interface::pcb_start(running_machine &machine, UINT8 *ciram_ptr, bool cart_mounted)
+{
+ // HACK: to reduce tagmap lookups for PPU-related IRQs, we add a hook to the
+ // main NES CPU here, even if it does not belong to this device.
+ m_maincpu = machine.device<cpu_device>("maincpu");
+
+ if (cart_mounted) // disksys expansion can arrive here without the memory banks!
+ {
+ // Setup PRG
+ m_prg_bank_mem[0] = machine.root_device().membank("prg0");
+ m_prg_bank_mem[1] = machine.root_device().membank("prg1");
+ m_prg_bank_mem[2] = machine.root_device().membank("prg2");
+ m_prg_bank_mem[3] = machine.root_device().membank("prg3");
+ for (int i = 0; i < 4; i++)
+ {
+ if (m_prg_bank_mem[i])
+ {
+ m_prg_bank_mem[i]->configure_entries(0, m_prg_size / 0x2000, m_prg, 0x2000);
+ m_prg_bank_mem[i]->set_entry(i);
+ m_prg_bank[i] = i;
+ }
+ }
+ }
+
+ // Setup CHR (VRAM can be present also without PRG rom)
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ chr8(0, m_chr_source);
+
+ // Setup NT
+ m_ciram = ciram_ptr;
+
+ if (m_four_screen_vram)
+ {
+ m_ext_ntram.resize(0x2000);
+ device().save_item(NAME(m_ext_ntram));
+ }
+
+ // at loading time we have configured m_mirroring, now setup NT pages
+ set_nt_mirroring(m_mirroring);
+
+ // save the on-cart RAM pointers
+ if (!m_prgram.empty())
+ device().save_item(NAME(m_prgram));
+ if (!m_vram.empty())
+ device().save_item(NAME(m_vram));
+ if (!m_battery.empty())
+ device().save_item(NAME(m_battery));
+}
+
+void device_nes_cart_interface::pcb_reg_postload(running_machine &machine)
+{
+ machine.save().register_postload(save_prepost_delegate(FUNC(device_nes_cart_interface::nes_banks_restore), this));
+}
+
+void device_nes_cart_interface::nes_banks_restore()
+{
+ for (int i = 0; i < 4; i++)
+ m_prg_bank_mem[i]->set_entry(m_prg_bank[i]);
+
+ for (int i = 0; i < 8; i++)
+ chr1_x(i, m_chr_orig[i] / 0x400, m_chr_src[i]);
+
+ for (int i = 0; i < 4; i++)
+ set_nt_page(i, m_nt_src[i], m_nt_orig[i] / 0x400, m_nt_writable[i]);
+}
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// nes_cart_slot_device - constructor
+//-------------------------------------------------
+nes_cart_slot_device::nes_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, NES_CART_SLOT, "NES Cartridge Slot", tag, owner, clock, "nes_cart_slot", __FILE__),
+ device_image_interface(mconfig, *this),
+ device_slot_interface(mconfig, *this),
+ m_crc_hack(0),
+ m_pcb_id(NO_BOARD),
+ m_must_be_loaded(1)
+{
+}
+
+//-------------------------------------------------
+// nes_cart_slot_device - destructor
+//-------------------------------------------------
+
+nes_cart_slot_device::~nes_cart_slot_device()
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void nes_cart_slot_device::device_start()
+{
+ m_cart = dynamic_cast<device_nes_cart_interface *>(get_card_device());
+}
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void nes_cart_slot_device::device_config_complete()
+{
+ // set brief and instance name
+ update_names();
+}
+
+
+void nes_cart_slot_device::pcb_start(UINT8 *ciram_ptr)
+{
+ if (m_cart)
+ m_cart->pcb_start(machine(), ciram_ptr, exists());
+}
+
+void nes_cart_slot_device::pcb_reset()
+{
+ if (m_cart)
+ m_cart->pcb_reset();
+}
+
+/*-------------------------------------------------
+ call load
+ -------------------------------------------------*/
+
+
+/*-------------------------------------------------
+
+ Load from xml list and identify the required slot device
+
+ -------------------------------------------------*/
+
+/* Include emulation of NES PCBs for softlist */
+#include "nes_pcb.inc"
+
+
+/*-------------------------------------------------
+
+ Load .unf files (UNIF boards) and identify the required slot device
+
+ -------------------------------------------------*/
+
+/* Include emulation of UNIF Boards for .unf files */
+#include "nes_unif.inc"
+
+
+/*-------------------------------------------------
+
+ Load .nes files (iNES mappers) and identify the required slot devices
+
+ -------------------------------------------------*/
+
+/* Include emulation of iNES Mappers for .nes files */
+#include "nes_ines.inc"
+
+
+bool nes_cart_slot_device::call_load()
+{
+ if (m_cart)
+ {
+ if (software_entry() == NULL)
+ {
+ char magic[4];
+
+ /* Check first 4 bytes of the image to decide if it is UNIF or iNES */
+ /* Unfortunately, many .unf files have been released as .nes, so we cannot rely on extensions only */
+ fread(magic, 4);
+
+ if ((magic[0] == 'N') && (magic[1] == 'E') && (magic[2] == 'S')) /* If header starts with 'NES' it is iNES */
+ {
+ if (length() <= 0x10)
+ {
+ logerror("%s only contains the iNES header and no data.\n", filename());
+ return IMAGE_INIT_FAIL;
+ }
+
+ call_load_ines();
+ }
+ else if ((magic[0] == 'U') && (magic[1] == 'N') && (magic[2] == 'I') && (magic[3] == 'F')) /* If header starts with 'UNIF' it is UNIF */
+ {
+ if (length() <= 0x20)
+ {
+ logerror("%s only contains the UNIF header and no data.\n", filename());
+ return IMAGE_INIT_FAIL;
+ }
+
+ call_load_unif();
+ }
+ else
+ {
+ logerror("%s is NOT a file in either iNES or UNIF format.\n", filename());
+ return IMAGE_INIT_FAIL;
+ }
+ }
+ else
+ call_load_pcb();
+ }
+
+ return IMAGE_INIT_PASS;
+}
+
+
+/*-------------------------------------------------
+ call_unload
+ -------------------------------------------------*/
+
+void nes_cart_slot_device::call_unload()
+{
+ if (m_cart)
+ {
+ if (m_cart->get_battery_size() || m_cart->get_mapper_sram_size())
+ {
+ UINT32 tot_size = m_cart->get_battery_size() + m_cart->get_mapper_sram_size();
+ dynamic_buffer temp_nvram(tot_size);
+ if (m_cart->get_battery_size())
+ memcpy(&temp_nvram[0], m_cart->get_battery_base(), m_cart->get_battery_size());
+ if (m_cart->get_mapper_sram_size())
+ memcpy(&temp_nvram[m_cart->get_battery_size()], m_cart->get_mapper_sram_base(), m_cart->get_mapper_sram_size());
+
+ battery_save(&temp_nvram[0], tot_size);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ call softlist load
+ -------------------------------------------------*/
+
+bool nes_cart_slot_device::call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry)
+{
+ load_software_part_region(*this, swlist, swname, start_entry);
+ return TRUE;
+}
+
+/*-------------------------------------------------
+ get default card software
+ -------------------------------------------------*/
+
+void nes_cart_slot_device::get_default_card_software(std::string &result)
+{
+ if (open_image_file(mconfig().options()))
+ {
+ const char *slot_string = "nrom";
+ UINT32 len = core_fsize(m_file);
+ dynamic_buffer rom(len);
+
+ core_fread(m_file, &rom[0], len);
+
+ if ((rom[0] == 'N') && (rom[1] == 'E') && (rom[2] == 'S'))
+ slot_string = get_default_card_ines(&rom[0], len);
+
+ if ((rom[0] == 'U') && (rom[1] == 'N') && (rom[2] == 'I') && (rom[3] == 'F'))
+ slot_string = get_default_card_unif(&rom[0], len);
+
+ clear();
+
+ result.assign(slot_string);
+ }
+ else
+ software_get_default_slot(result, "nrom");
+}
+
+
+/*-------------------------------------------------
+ read
+ -------------------------------------------------*/
+
+READ8_MEMBER(nes_cart_slot_device::read_l)
+{
+ if (m_cart)
+ {
+ UINT8 val = m_cart->read_l(space, offset);
+ // update open bus
+ m_cart->set_open_bus(((offset + 0x4100) & 0xff00) >> 8);
+ return val;
+ }
+ else
+ return 0xff;
+}
+
+READ8_MEMBER(nes_cart_slot_device::read_m)
+{
+ if (m_cart)
+ {
+ UINT8 val = m_cart->read_m(space, offset);
+ // update open bus
+ m_cart->set_open_bus(((offset + 0x6000) & 0xff00) >> 8);
+ return val;
+ }
+ else
+ return 0xff;
+}
+
+READ8_MEMBER(nes_cart_slot_device::read_h)
+{
+ if (m_cart)
+ {
+ UINT8 val = m_cart->read_h(space, offset);
+ // update open bus
+ m_cart->set_open_bus(((offset + 0x8000) & 0xff00) >> 8);
+ return val;
+ }
+ else
+ return 0xff;
+}
+
+READ8_MEMBER(nes_cart_slot_device::read_ex)
+{
+ if (m_cart)
+ {
+ UINT8 val = m_cart->read_ex(space, offset);
+ // update open bus
+ m_cart->set_open_bus(((offset + 0x4020) & 0xff00) >> 8);
+ return val;
+ }
+ else
+ return 0xff;
+}
+
+
+/*-------------------------------------------------
+ write
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_cart_slot_device::write_l)
+{
+ if (m_cart)
+ {
+ m_cart->write_l(space, offset, data);
+ // update open bus
+ m_cart->set_open_bus(((offset + 0x4100) & 0xff00) >> 8);
+ }
+}
+
+WRITE8_MEMBER(nes_cart_slot_device::write_m)
+{
+ if (m_cart)
+ {
+ m_cart->write_m(space, offset, data);
+ // update open bus
+ m_cart->set_open_bus(((offset + 0x6000) & 0xff00) >> 8);
+ }
+}
+
+WRITE8_MEMBER(nes_cart_slot_device::write_h)
+{
+ if (m_cart)
+ {
+ m_cart->write_h(space, offset, data);
+ // update open bus
+ m_cart->set_open_bus(((offset + 0x8000) & 0xff00) >> 8);
+ }
+}
+
+WRITE8_MEMBER(nes_cart_slot_device::write_ex)
+{
+ if (m_cart)
+ {
+ m_cart->write_ex(space, offset, data);
+ // update open bus
+ m_cart->set_open_bus(((offset + 0x4020) & 0xff00) >> 8);
+ }
+}
+
+
+//-------------------------------------------------
+// partial hash function to be used by
+// device_image_partialhash_func
+//-------------------------------------------------
+
+void nes_partialhash(hash_collection &dest, const unsigned char *data,
+ unsigned long length, const char *functions)
+{
+ if (length <= 16)
+ return;
+ dest.compute(&data[16], length - 16, functions);
+}
diff --git a/src/devices/bus/nes/nes_slot.h b/src/devices/bus/nes/nes_slot.h
new file mode 100644
index 00000000000..f959465af33
--- /dev/null
+++ b/src/devices/bus/nes/nes_slot.h
@@ -0,0 +1,430 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_SLOT_H__
+#define __NES_SLOT_H__
+
+/***************************************************************************
+ TYPE DEFINITIONS
+ ***************************************************************************/
+
+// uncomment this for *very* verbose logging of most cart accesses
+//#define NES_PCB_DEBUG
+
+/* Boards */
+enum
+{
+ STD_NROM = 0,
+ STD_AXROM, STD_AMROM, STD_BXROM,
+ STD_CNROM, STD_CPROM,
+ STD_EXROM, STD_FXROM, STD_GXROM,
+ STD_HKROM, STD_PXROM,
+ STD_SXROM, STD_TXROM, STD_TXSROM,
+ STD_TKROM, STD_TQROM,
+ STD_UXROM, STD_UN1ROM, UXROM_CC,
+ HVC_FAMBASIC, NES_QJ, PAL_ZZ, STD_EVENT,
+ STD_SXROM_A, STD_SOROM, STD_SOROM_A,
+ STD_DISKSYS,
+ STD_NROM368,//homebrew extension of NROM!
+ /* Discrete components boards (by various manufacturer) */
+ DIS_74X161X138, DIS_74X139X74,
+ DIS_74X377, DIS_74X161X161X32,
+ /* Active Enterprises */
+ ACTENT_ACT52,
+ /* AGCI */
+ AGCI_50282,
+ /* AVE */
+ AVE_NINA01, AVE_NINA06, AVE_MAXI15,
+ /* Bandai */
+ BANDAI_FJUMP2, BANDAI_PT554,
+ BANDAI_DATACH, BANDAI_KARAOKE, BANDAI_OEKAKIDS,
+ BANDAI_FCG, BANDAI_LZ93, BANDAI_LZ93EX1, BANDAI_LZ93EX2,
+ /* Caltron */
+ CALTRON_6IN1,
+ /* Camerica */
+ CAMERICA_BF9093, CAMERICA_BF9096, CAMERICA_ALADDIN,
+ CAMERICA_GOLDENFIVE, GG_NROM,
+ /* Dreamtech */
+ DREAMTECH_BOARD,
+ /* Irem */
+ IREM_G101, IREM_H3001, IREM_LROG017,
+ IREM_TAM_S1, IREM_HOLYDIVR,
+ /* Jaleco */
+ JALECO_SS88006, JALECO_JF11, JALECO_JF13,
+ JALECO_JF16, JALECO_JF17, JALECO_JF17_ADPCM,
+ JALECO_JF19, JALECO_JF19_ADPCM, JALECO_JF23,
+ JALECO_JF24, JALECO_JF29, JALECO_JF33,
+ /* Konami */
+ KONAMI_VRC1, KONAMI_VRC2, KONAMI_VRC3,
+ KONAMI_VRC4, KONAMI_VRC6, KONAMI_VRC7,
+ /* Namcot */
+ NAMCOT_163, NAMCOT_175, NAMCOT_340,
+ NAMCOT_3425, NAMCOT_34X3, NAMCOT_3446,
+ /* NTDEC */
+ NTDEC_ASDER, NTDEC_FIGHTINGHERO,
+ /* Rex Soft */
+ REXSOFT_SL1632, REXSOFT_DBZ5,
+ /* Sachen */
+ SACHEN_8259A, SACHEN_8259B, SACHEN_8259C, SACHEN_8259D,
+ SACHEN_SA009, SACHEN_SA0036, SACHEN_SA0037,
+ SACHEN_SA72007, SACHEN_SA72008, SACHEN_TCA01,
+ SACHEN_TCU01, SACHEN_TCU02, SACHEN_SA9602B,
+ SACHEN_74LS374, SACHEN_74LS374_ALT, SACHEN_SHERO,
+ /* Sunsoft */
+ SUNSOFT_1, SUNSOFT_2, SUNSOFT_3, SUNSOFT_4,
+ SUNSOFT_DCS, SUNSOFT_5, SUNSOFT_FME7,
+ /* Taito */
+ TAITO_TC0190FMC, TAITO_TC0190FMCP,
+ TAITO_X1_005, TAITO_X1_017,
+ /* Tengen */
+ TENGEN_800008, TENGEN_800032, TENGEN_800037,
+ /* TXC */
+ TXC_22211, TXC_DUMARACING, TXC_MJBLOCK,
+ TXC_COMMANDOS, TXC_TW, TXC_STRIKEW,
+ /* Multigame Carts */
+ BMC_64IN1NR, BMC_190IN1, BMC_A65AS,
+ BMC_HIK8IN1, BMC_NOVEL1, BMC_NOVEL2, BMC_S24IN1SC03, BMC_T262,
+ BMC_WS, BMC_SUPERBIG_7IN1, BMC_SUPERHIK_4IN1, BMC_BALLGAMES_11IN1,
+ BMC_MARIOPARTY_7IN1, BMC_GOLD_7IN1, BMC_SUPER_700IN1, BMC_FAMILY_4646,
+ BMC_36IN1, BMC_21IN1, BMC_150IN1, BMC_35IN1, BMC_64IN1,
+ BMC_15IN1, BMC_SUPERHIK_300IN1, BMC_SUPERGUN_20IN1,
+ BMC_GOLDENCARD_6IN1, BMC_72IN1, BMC_SUPER_42IN1, BMC_76IN1,
+ BMC_31IN1, BMC_22GAMES, BMC_20IN1, BMC_110IN1,
+ BMC_70IN1, BMC_800IN1, BMC_1200IN1,
+ BMC_GKA, BMC_GKB, BMC_VT5201, BMC_BENSHIENG, BMC_810544,
+ BMC_NTD_03, BMC_G63IN1, BMC_FK23C, BMC_FK23CA, BMC_PJOY84,
+ BMC_POWERFUL_255, BMC_11160, BMC_G146, BMC_8157, BMC_830118C,
+ BMC_411120C, BMC_GOLD150, BMC_GOLD260, BMC_CH001, BMC_SUPER22,
+ BMC_12IN1, BMC_4IN1RESET, BMC_42IN1RESET,
+ /* Unlicensed */
+ UNL_8237, UNL_CC21, UNL_AX5705, UNL_KOF97, UNL_KS7057,
+ UNL_N625092, UNL_SC127, UNL_SMB2J, UNL_T230, UNL_MMALEE,
+ UNL_UXROM, UNL_MK2, UNL_XIAOZY, UNL_KOF96,
+ UNL_SF3, UNL_RACERMATE, UNL_EDU2K, UNL_LH53, UNL_LH32, UNL_LH10,
+ UNL_STUDYNGAME, UNL_603_5052, UNL_H2288, UNL_2708,
+ UNL_MALISB, UNL_BB, UNL_AC08, UNL_A9746, UNL_WORLDHERO,
+ UNL_43272, UNL_TF1201, UNL_CITYFIGHT,
+ /* Bootleg boards */
+ BTL_SMB2JA, BTL_MARIOBABY, BTL_AISENSHINICOL, BTL_TOBIDASE,
+ BTL_SMB2JB, BTL_09034A, BTL_SMB3, BTL_SBROS11, BTL_DRAGONNINJA,
+ BTL_PIKACHUY2K, BTL_SHUIGUAN,
+ /* Misc: these are needed to convert mappers to boards, I will sort them later */
+ OPENCORP_DAOU306, HES_BOARD, SVISION16_BOARD, RUMBLESTATION_BOARD, JYCOMPANY_A, JYCOMPANY_B, JYCOMPANY_C,
+ MAGICSERIES_MD, KASING_BOARD, FUTUREMEDIA_BOARD, FUKUTAKE_BOARD, SOMARI_SL12,
+ HENGG_SRICH, HENGG_XHZS, HENGG_SHJY3, SUBOR_TYPE0, SUBOR_TYPE1,
+ KAISER_KS7058, KAISER_KS7032, KAISER_KS7022, KAISER_KS7017,
+ KAISER_KS7012, KAISER_KS7013B, KAISER_KS202, KAISER_KS7031,
+ CNE_DECATHLON, CNE_FSB, CNE_SHLZ, CONY_BOARD, YOKO_BOARD,
+ RCM_GS2015, RCM_GS2004, RCM_GS2013, RCM_TF9IN1, RCM_3DBLOCK,
+ WAIXING_TYPE_A, WAIXING_TYPE_A1, WAIXING_TYPE_B, WAIXING_TYPE_C, WAIXING_TYPE_D,
+ WAIXING_TYPE_E, WAIXING_TYPE_F, WAIXING_TYPE_G, WAIXING_TYPE_H, WAIXING_TYPE_H1,
+ WAIXING_TYPE_I, WAIXING_TYPE_J, WAIXING_FS304,
+ WAIXING_SGZLZ, WAIXING_SGZ, WAIXING_WXZS, WAIXING_SECURITY, WAIXING_SH2,
+ WAIXING_DQ8, WAIXING_FFV, WAIXING_WXZS2, SUPERGAME_LIONKING, SUPERGAME_BOOGERMAN,
+ KAY_BOARD, HOSENKAN_BOARD, NITRA_TDA, GOUDER_37017, NANJING_BOARD,
+ WHIRLWIND_2706,
+ NOCASH_NOCHR, // homebrew PCB design which uses NTRAM for CHRRAM
+ BTL_ACTION53, // homebrew PCB for homebrew multicarts
+ BTL_2A03_PURITANS, // homebrew PCB
+ /* FFE boards, for mappers 6, 8, 17 */
+ FFE3_BOARD, FFE4_BOARD, FFE8_BOARD, TEST_BOARD,
+ /* Unsupported (for place-holder boards, with no working emulation) & no-board (at init) */
+ UNSUPPORTED_BOARD, UNKNOWN_BOARD, NO_BOARD
+};
+
+
+#define CHRROM 0
+#define CHRRAM 1
+
+
+#define CIRAM 0
+#define VROM 1
+#define EXRAM 2
+#define MMC5FILL 3
+#define CART_NTRAM 4
+
+
+#define PPU_MIRROR_NONE 0
+#define PPU_MIRROR_VERT 1
+#define PPU_MIRROR_HORZ 2
+#define PPU_MIRROR_HIGH 3
+#define PPU_MIRROR_LOW 4
+#define PPU_MIRROR_4SCREEN 5
+
+
+// ======================> device_nes_cart_interface
+
+class device_nes_cart_interface : public device_slot_card_interface
+{
+public:
+ // construction/destruction
+ device_nes_cart_interface(const machine_config &mconfig, device_t &device);
+ virtual ~device_nes_cart_interface();
+
+ // reading and writing
+ virtual DECLARE_READ8_MEMBER(read_l);
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_READ8_MEMBER(read_h) { return 0xff; }
+ virtual DECLARE_READ8_MEMBER(read_ex) { return 0xff; }
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+ virtual DECLARE_WRITE8_MEMBER(write_ex) { }
+
+ virtual DECLARE_READ8_MEMBER(chr_r);
+ virtual DECLARE_WRITE8_MEMBER(chr_w);
+ virtual DECLARE_READ8_MEMBER(nt_r);
+ virtual DECLARE_WRITE8_MEMBER(nt_w);
+
+ // hack until disk system is made modern!
+ virtual void disk_flip_side() { }
+
+ void prg_alloc(size_t size, const char *tag);
+ void vrom_alloc(size_t size, const char *tag);
+ void prgram_alloc(size_t size);
+ void vram_alloc(size_t size);
+ void battery_alloc(size_t size);
+
+ int get_mirroring() { return m_mirroring; }
+ void set_mirroring(int val) { m_mirroring = val; }
+ bool get_pcb_ctrl_mirror() { return m_pcb_ctrl_mirror; }
+ void set_pcb_ctrl_mirror(bool val) { m_pcb_ctrl_mirror = val; }
+ bool get_four_screen_vram() { return m_four_screen_vram; }
+ void set_four_screen_vram(bool val) { m_four_screen_vram = val; }
+ bool get_trainer() { return m_has_trainer; }
+ void set_trainer(bool val) { m_has_trainer = val; }
+
+ void set_ce(int mask, int state) { m_ce_mask = mask; m_ce_state = state; }
+ void set_vrc_lines(int PRG_A, int PRG_B, int CHR) { m_vrc_ls_prg_a = PRG_A; m_vrc_ls_prg_b = PRG_B; m_vrc_ls_chr = CHR; }
+ void set_x1_005_alt(bool val) { m_x1_005_alt_mirroring = val; }
+ void set_bus_conflict(bool val) { m_bus_conflict = val; }
+ void set_open_bus(UINT8 val) { m_open_bus = val; }
+
+ UINT8* get_prg_base() { return m_prg; }
+ UINT8* get_prgram_base() { return &m_prgram[0]; }
+ UINT8* get_vrom_base() { return m_vrom; }
+ UINT8* get_vram_base() { return &m_vram[0]; }
+ UINT8* get_battery_base() { return &m_battery[0]; }
+ UINT8* get_mapper_sram_base() { return m_mapper_sram; }
+
+ UINT32 get_prg_size() { return m_prg_size; }
+ UINT32 get_prgram_size() { return m_prgram.size(); }
+ UINT32 get_vrom_size() { return m_vrom_size; }
+ UINT32 get_vram_size() { return m_vram.size(); }
+ UINT32 get_battery_size() { return m_battery.size(); }
+ UINT32 get_mapper_sram_size() { return m_mapper_sram_size; }
+
+ virtual void ppu_latch(offs_t offset) {}
+ virtual void hblank_irq(int scanline, int vblank, int blanked) {}
+ virtual void scanline_irq(int scanline, int vblank, int blanked) {}
+
+ virtual void pcb_reset() {} // many pcb expect specific PRG/CHR banking at start
+ virtual void pcb_start(running_machine &machine, UINT8 *ciram_ptr, bool cart_mounted);
+ void pcb_reg_postload(running_machine &machine);
+ void nes_banks_restore();
+
+ UINT8 hi_access_rom(UINT32 offset); // helper ROM access for a bunch of PCB reading 0x8000-0xffff for protection too
+ UINT8 account_bus_conflict(UINT32 offset, UINT8 data);
+
+protected:
+
+ // internal state
+ UINT8 *m_prg;
+ UINT8 *m_vrom;
+ UINT8 *m_ciram;
+ dynamic_buffer m_prgram;
+ dynamic_buffer m_vram;
+ dynamic_buffer m_battery;
+ UINT32 m_prg_size;
+ UINT32 m_vrom_size;
+
+ // HACK: to reduce tagmap lookups for PPU-related IRQs, we add a hook to the
+ // main NES CPU here, even if it does not belong to this device.
+ cpu_device *m_maincpu;
+
+ // these are specific of some boards but must be accessible from the driver
+ // E.g. additional save ram for HKROM, X1-005 & X1-017 boards, or ExRAM for MMC5
+ UINT8 *m_mapper_sram;
+ dynamic_buffer m_ext_ntram;
+ UINT32 m_mapper_sram_size;
+
+ int m_ce_mask;
+ int m_ce_state;
+ int m_vrc_ls_prg_a;
+ int m_vrc_ls_prg_b;
+ int m_vrc_ls_chr;
+
+ int m_mirroring;
+ bool m_pcb_ctrl_mirror, m_four_screen_vram, m_has_trainer;
+ bool m_x1_005_alt_mirroring; // temp hack for two kind of mirroring in Taito X1-005 boards (to be replaced with pin checking)
+ bool m_bus_conflict;
+ UINT8 m_open_bus;
+
+ // PRG
+ inline int prg_8k_bank_num(int bank);
+ inline void update_prg_banks(int prg_bank_start, int prg_bank_end);
+ memory_bank *m_prg_bank_mem[4];
+ int m_prg_bank[4];
+ UINT32 m_prg_chunks;
+ UINT32 m_prg_mask;
+
+ // PRG helpers
+ void prg32(int bank);
+ void prg16_89ab(int bank);
+ void prg16_cdef(int bank);
+ void prg8_89(int bank);
+ void prg8_ab(int bank);
+ void prg8_cd(int bank);
+ void prg8_ef(int bank);
+ void prg8_x(int start, int bank);
+
+
+ // CHR
+ int m_chr_source; // global source for the 8 VROM banks
+ inline void chr_sanity_check(int source);
+
+ //these were previously called chr_map. they are a quick banking structure,
+ //because some of these change multiple times per scanline!
+ int m_chr_src[8]; //defines source of base pointer
+ int m_chr_orig[8]; //defines offset of 0x400 byte segment at base pointer
+ UINT8 *m_chr_access[8]; //source translated + origin -> valid pointer!
+
+ UINT32 m_vrom_chunks;
+ UINT32 m_vram_chunks;
+
+ // CHR helpers
+ void chr8(int bank, int source);
+ void chr4_x(int start, int bank, int source);
+ void chr4_0(int bank, int source){ chr4_x(0, bank, source); };
+ void chr4_4(int bank, int source){ chr4_x(4, bank, source); };
+ void chr2_x(int start, int bank, int source);
+ void chr2_0(int bank, int source) { chr2_x(0, bank, source); };
+ void chr2_2(int bank, int source) { chr2_x(2, bank, source); };
+ void chr2_4(int bank, int source) { chr2_x(4, bank, source); };
+ void chr2_6(int bank, int source) { chr2_x(6, bank, source); };
+ void chr1_x(int start, int bank, int source);
+ void chr1_0(int bank, int source) { chr1_x(0, bank, source); };
+ void chr1_1(int bank, int source) { chr1_x(1, bank, source); };
+ void chr1_2(int bank, int source) { chr1_x(2, bank, source); };
+ void chr1_3(int bank, int source) { chr1_x(3, bank, source); };
+ void chr1_4(int bank, int source) { chr1_x(4, bank, source); };
+ void chr1_5(int bank, int source) { chr1_x(5, bank, source); };
+ void chr1_6(int bank, int source) { chr1_x(6, bank, source); };
+ void chr1_7(int bank, int source) { chr1_x(7, bank, source); };
+
+
+ // NameTable & Mirroring
+ //these were previously called nt_page. they are a quick banking structure for a maximum of 4K of RAM/ROM/ExRAM
+ int m_nt_src[4];
+ int m_nt_orig[4];
+ int m_nt_writable[4];
+ UINT8 *m_nt_access[4]; //quick banking structure for a maximum of 4K of RAM/ROM/ExRAM
+
+ void set_nt_page(int page, int source, int bank, int writable);
+ void set_nt_mirroring(int mirroring);
+
+ std::vector<UINT16> m_prg_bank_map;
+};
+
+void nes_partialhash(hash_collection &dest, const unsigned char *data, unsigned long length, const char *functions);
+
+// ======================> nes_cart_slot_device
+
+class nes_cart_slot_device : public device_t,
+ public device_image_interface,
+ public device_slot_interface
+{
+public:
+ // construction/destruction
+ nes_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ virtual ~nes_cart_slot_device();
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_config_complete();
+
+ // image-level overrides
+ virtual bool call_load();
+ virtual void call_unload();
+ virtual bool call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry);
+
+ void call_load_ines();
+ void call_load_unif();
+ void call_load_pcb();
+
+ virtual iodevice_t image_type() const { return IO_CARTSLOT; }
+ virtual bool is_readable() const { return 1; }
+ virtual bool is_writeable() const { return 0; }
+ virtual bool is_creatable() const { return 0; }
+ virtual bool must_be_loaded() const { return m_must_be_loaded; }
+ virtual bool is_reset_on_load() const { return 1; }
+ virtual const char *image_interface() const { return "nes_cart"; }
+ virtual const char *file_extensions() const { return "nes,unf,unif"; }
+ virtual const option_guide *create_option_guide() const { return NULL; }
+ virtual device_image_partialhash_func get_partial_hash() const { return &nes_partialhash; }
+
+ // slot interface overrides
+ virtual void get_default_card_software(std::string &result);
+ const char * get_default_card_ines(UINT8 *ROM, UINT32 len);
+ const char * get_default_card_unif(UINT8 *ROM, UINT32 len);
+ const char * nes_get_slot(int pcb_id);
+ int nes_get_pcb_id(const char *slot);
+
+ // reading and writing
+ virtual DECLARE_READ8_MEMBER(read_l);
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_READ8_MEMBER(read_h);
+ virtual DECLARE_READ8_MEMBER(read_ex);
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+ virtual DECLARE_WRITE8_MEMBER(write_ex);
+
+ // hack until disk system is made modern!
+ virtual void disk_flip_side() { if (m_cart) m_cart->disk_flip_side(); }
+
+ int get_pcb_id() { return m_pcb_id; };
+
+ void pcb_start(UINT8 *ciram_ptr);
+ void pcb_reset();
+
+ // temporarily here
+ int m_crc_hack;
+
+ int get_crc_hack() { return m_crc_hack; };
+
+ void set_must_be_loaded(bool _must_be_loaded) { m_must_be_loaded = _must_be_loaded; }
+
+ //private:
+
+ device_nes_cart_interface* m_cart;
+ int m_pcb_id;
+ bool m_must_be_loaded;
+};
+
+// device type definition
+extern const device_type NES_CART_SLOT;
+
+
+/***************************************************************************
+ DEVICE CONFIGURATION MACROS
+ ***************************************************************************/
+
+#define NESSLOT_PRGROM_REGION_TAG ":cart:prg_rom"
+#define NESSLOT_CHRROM_REGION_TAG ":cart:chr_rom"
+
+
+#define MCFG_NES_CARTRIDGE_ADD(_tag, _slot_intf, _def_slot) \
+ MCFG_DEVICE_ADD(_tag, NES_CART_SLOT, 0) \
+ MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false)
+
+#define MCFG_NES_CARTRIDGE_NOT_MANDATORY \
+ static_cast<nes_cart_slot_device *>(device)->set_must_be_loaded(FALSE);
+
+
+// Hacky configuration to add a slot with fixed disksys interface
+#define MCFG_DISKSYS_ADD(_tag, _slot_intf, _def_slot) \
+ MCFG_DEVICE_ADD(_tag, NES_CART_SLOT, 0) \
+ MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, true) \
+ MCFG_NES_CARTRIDGE_NOT_MANDATORY
+
+#endif
diff --git a/src/devices/bus/nes/nes_unif.inc b/src/devices/bus/nes/nes_unif.inc
new file mode 100644
index 00000000000..b501c590159
--- /dev/null
+++ b/src/devices/bus/nes/nes_unif.inc
@@ -0,0 +1,575 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/*****************************************************************************************
+
+ NES MMC Emulation - UNIF boards
+
+ Very preliminary support for UNIF boards
+
+ TODO:
+ - properly support WRAM, etc.
+
+****************************************************************************************/
+
+
+/* Set to generate prg & chr files when the cart is loaded */
+#define SPLIT_PRG 0
+#define SPLIT_CHR 0
+
+
+/*************************************************************
+
+ unif_list
+
+ Supported UNIF boards and corresponding handlers
+
+ *************************************************************/
+
+
+struct unif
+{
+ const char *board; /* UNIF board */
+
+ int nvwram;
+ int wram;
+ int chrram;
+ int board_idx;
+};
+
+
+/* CHRRAM sizes */
+enum
+{
+ CHRRAM_0 = 0,
+ CHRRAM_1,
+ CHRRAM_2,
+ CHRRAM_4,
+ CHRRAM_6,
+ CHRRAM_8,
+ CHRRAM_16,
+ CHRRAM_32
+};
+
+static const unif unif_list[] =
+{
+/* UNIF NVW WRAM CRAM IDX*/
+ { "DREAMTECH01", 0, 0, CHRRAM_8, DREAMTECH_BOARD}, //UNIF only!
+ { "NES-ANROM", 0, 0, CHRRAM_8, STD_AXROM},
+ { "NES-AOROM", 0, 0, CHRRAM_8, STD_AXROM},
+ { "NES-CNROM", 0, 0, CHRRAM_0, STD_CNROM},
+ { "NES-NROM", 0, 0, CHRRAM_0, STD_NROM},
+ { "NES-NROM-128", 0, 0, CHRRAM_0, STD_NROM},
+ { "NES-NROM-256", 0, 0, CHRRAM_0, STD_NROM},
+ { "NES-NTBROM", 8, 0, CHRRAM_0, SUNSOFT_DCS},
+ { "NES-SLROM", 0, 0, CHRRAM_0, STD_SXROM},
+ { "NES-TBROM", 0, 0, CHRRAM_0, STD_TXROM},
+ { "NES-TFROM", 0, 0, CHRRAM_0, STD_TXROM},
+ { "NES-TKROM", 8, 0, CHRRAM_0, STD_TXROM},
+ { "NES-TLROM", 0, 0, CHRRAM_0, STD_TXROM},
+ { "NES-UOROM", 0, 0, CHRRAM_8, STD_UXROM},
+ { "UNL-22211", 0, 0, CHRRAM_0, TXC_22211},
+ // mapper 172 & 173 are variant of this one... no UNIF?
+ { "UNL-KOF97", 0, 0, CHRRAM_0, UNL_KOF97},
+ { "UNL-SA-NROM", 0, 0, CHRRAM_0, SACHEN_TCA01},
+ { "UNL-VRC7", 0, 0, CHRRAM_0, KONAMI_VRC7},
+ { "UNL-T-230", 0, 0, CHRRAM_8, UNL_T230},
+ { "UNL-CC-21", 0, 0, CHRRAM_0, UNL_CC21},
+ { "UNL-AX5705", 0, 0, CHRRAM_0, UNL_AX5705},
+ { "UNL-SMB2J", 8, 8, CHRRAM_0, UNL_SMB2J},
+ { "UNL-8237", 0, 0, CHRRAM_0, UNL_8237},
+ { "UNL-SL1632", 0, 0, CHRRAM_0, REXSOFT_SL1632},
+ { "UNL-SACHEN-74LS374N", 0, 0, CHRRAM_0, SACHEN_74LS374},
+ // mapper 243 variant exists! how to distinguish?!? mapper243_l_w, NULL, NULL, NULL, NULL, NULL, NULL (also uses NT_VERT!)
+ { "UNL-TC-U01-1.5M", 0, 0, CHRRAM_0, SACHEN_TCU01},
+ { "UNL-SACHEN-8259C", 0, 0, CHRRAM_0, SACHEN_8259C},
+ { "UNL-SA-016-1M", 0, 0, CHRRAM_0, AVE_NINA06}, // actually this is Mapper 146, but works like 79!
+ { "UNL-SACHEN-8259D", 0, 0, CHRRAM_0, SACHEN_8259D},
+ { "UNL-SA-72007", 0, 0, CHRRAM_0, SACHEN_SA72007},
+ { "UNL-SA-72008", 0, 0, CHRRAM_0, SACHEN_SA72008},
+ { "UNL-SA-0037", 0, 0, CHRRAM_0, SACHEN_SA0037},
+ { "UNL-SA-0036", 0, 0, CHRRAM_0, SACHEN_SA0036},
+ { "UNL-SA-9602B", 0, 0, CHRRAM_0, SACHEN_SA9602B},
+ { "UNL-SACHEN-8259A", 0, 0, CHRRAM_0, SACHEN_8259A},
+ { "UNL-SACHEN-8259B", 0, 0, CHRRAM_0, SACHEN_8259B},
+ { "BMC-190IN1", 0, 0, CHRRAM_0, BMC_190IN1},
+ { "BMC-64IN1NOREPEAT", 0, 0, CHRRAM_0, BMC_64IN1NR}, //UNIF only!
+ { "BMC-A65AS", 0, 0, CHRRAM_8, BMC_A65AS}, //UNIF only!
+ { "BMC-GS-2004", 0, 0, CHRRAM_8, RCM_GS2004}, //UNIF only!
+ { "BMC-GS-2013", 0, 0, CHRRAM_8, RCM_GS2013}, //UNIF only!
+ { "BMC-NOVELDIAMOND9999999IN1", 0, 0, CHRRAM_0, BMC_NOVEL1},
+ { "BMC-SUPER24IN1SC03", 8, 0, CHRRAM_8, BMC_S24IN1SC03},
+ { "BMC-SUPERHIK8IN1", 8, 0, CHRRAM_0, BMC_HIK8IN1},
+ { "BMC-T-262", 0, 0, CHRRAM_8, BMC_T262}, //UNIF only!
+ { "BMC-WS", 0, 0, CHRRAM_0, BMC_WS}, //UNIF only!
+ { "BMC-N625092", 0, 0, CHRRAM_0, UNL_N625092},
+ // below are boards which are not yet supported, but are used by some UNIF files. they are here as a reminder to what is missing to be added
+ { "UNL-TEK90", 0, 0, CHRRAM_0, JYCOMPANY_A}, // JY Company A (is TEK90 the real PCB name?)
+ { "UNL-KS7017", 0, 0, CHRRAM_0, KAISER_KS7017},
+ { "UNL-KS7032", 0, 0, CHRRAM_0, KAISER_KS7032}, // mapper 142
+ { "UNL-603-5052", 0, 0, CHRRAM_0, UNL_603_5052}, // mapper 238?
+ { "UNL-EDU2000", 32, 0, CHRRAM_8, UNL_EDU2K},
+ { "UNL-H2288", 0, 0, CHRRAM_0, UNL_H2288}, // mapper 123
+ { "UNL-SHERO", 0, 0, CHRRAM_8, SACHEN_SHERO},
+ { "UNL-YOKO", 0, 0, CHRRAM_0, YOKO_BOARD}, // similar to mapper 83, but not the same
+ { "UNL-FS304", 0, 8, CHRRAM_8, WAIXING_FS304}, // used in Zelda 3 by Waixing
+ { "UNL-43272", 0, 0, CHRRAM_0, UNL_43272}, // used in Gaau Hok Gwong Cheung
+ { "BTL-MARIO1-MALEE2", 0, 0, CHRRAM_0, UNL_MMALEE}, // mapper 55?
+ { "BMC-FK23C", 0, 0, CHRRAM_0, BMC_FK23C},
+ { "BMC-FK23CA", 0, 0, CHRRAM_0, BMC_FK23CA},
+ { "BMC-GHOSTBUSTERS63IN1", 0, 0, CHRRAM_8, BMC_G63IN1 },
+ { "BMC-BS-5", 0, 0, CHRRAM_0, BMC_BENSHIENG},
+ { "BMC-810544-C-A1", 0, 0, CHRRAM_0, BMC_810544},
+ { "BMC-411120-C", 0, 0, CHRRAM_0, BMC_411120C},
+ { "BMC-8157", 0, 0, CHRRAM_8, BMC_8157},
+ { "BMC-830118C", 0, 0, CHRRAM_0, BMC_830118C},
+ { "BMC-D1038", 0, 0, CHRRAM_0, BMC_VT5201}, // mapper 60?
+ { "BMC-SUPERVISION16IN1", 0, 0, CHRRAM_0, SVISION16_BOARD}, // mapper 53
+ { "BMC-NTD-03", 0, 0, CHRRAM_0, BMC_NTD_03},
+ { "UNL-AC08", 0, 0, CHRRAM_0, UNL_AC08},
+ { "UNL-BB", 0, 0, CHRRAM_0, UNL_BB},
+ { "UNL-LH32", 0, 0, CHRRAM_0, UNL_LH32},
+ { "UNL-LH53", 0, 0, CHRRAM_0, UNL_LH53},
+ { "BMC-G-146", 0, 0, CHRRAM_0, BMC_G146},
+ { "BMC-11160", 0, 0, CHRRAM_0, BMC_11160},
+ { "UNL-MALISB", 0, 0, CHRRAM_0, UNL_MALISB},
+ { "UNL-TF1201", 0, 0, CHRRAM_0, UNL_TF1201},
+ { "BMC-12-IN-1", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD},
+ { "BMC-70IN1", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, // mapper 236?
+ { "BMC-70IN1B", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, // mapper 236?
+ { "BMC-42IN1RESETSWITCH", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, // mapper 60?
+ { "PEC-586", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD},
+ { "UNL-DANCE", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD},
+ { "UNL-DRIPGAME", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}, // [by Quietust - we need more info]
+ { "UNL-CITYFIGHT", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD},
+ { "UNL-OneBus", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD},
+ { "UNL-DANCE2000", 0, 0, CHRRAM_0, UNSUPPORTED_BOARD}
+};
+
+const unif *nes_unif_lookup( const char *board )
+{
+ int i;
+ for (i = 0; i < ARRAY_LENGTH(unif_list); i++)
+ {
+ if (!core_stricmp(unif_list[i].board, board))
+ return &unif_list[i];
+ }
+ return NULL;
+}
+
+/*************************************************************
+
+ unif_mapr_setup
+
+ setup the board specific variables (wram, nvwram, pcb_id etc.)
+ for a given board (after reading the MAPR chunk of the UNIF file)
+
+ *************************************************************/
+
+void unif_mapr_setup( const char *board, int *pcb_id, int *battery, int *prgram, int *vram_chunks )
+{
+ const unif *unif_board = nes_unif_lookup(board);
+ if (unif_board == NULL)
+ fatalerror("Unknown UNIF board %s.\n", board);
+
+ *pcb_id = unif_board->board_idx;
+ *battery = unif_board->nvwram; // we should implement battery banks based on the size of this...
+ *prgram = unif_board->wram; // we should implement WRAM banks based on the size of this...
+
+ if (unif_board->chrram <= CHRRAM_8)
+ *vram_chunks = 1;
+ else if (unif_board->chrram == CHRRAM_16)
+ *vram_chunks = 2;
+ else if (unif_board->chrram == CHRRAM_32)
+ *vram_chunks = 4;
+}
+
+
+/*************************************************************
+
+ call_load_unif
+
+ *************************************************************/
+
+void nes_cart_slot_device::call_load_unif()
+{
+ UINT32 vram_size = 0, prgram_size = 0, battery_size = 0, mapper_sram_size = 0;
+ // SETUP step 1: running through the file and getting PRG, VROM sizes
+ UINT32 unif_ver = 0, chunk_length = 0, read_length = 0x20;
+ UINT32 prg_start = 0, chr_start = 0;
+ UINT32 size = length(), prg_size = 0, vrom_size = 0;
+ UINT8 buffer[4], mirror = 0;
+ char magic2[4];
+ char unif_mapr[32]; // here we should store MAPR chunks
+ bool mapr_chunk_found = FALSE, small_prg = FALSE;
+
+ // allocate space to temporarily store PRG & CHR banks
+ dynamic_buffer temp_prg(256 * 0x4000);
+ dynamic_buffer temp_chr(256 * 0x2000);
+ UINT8 temp_byte = 0;
+
+ fseek(4, SEEK_SET);
+ fread(&buffer, 4);
+ unif_ver = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+ logerror("Loaded game in UNIF format, version %d\n", unif_ver);
+
+ do
+ {
+ fseek(read_length, SEEK_SET);
+
+ memset(magic2, '\0', sizeof(magic2));
+ fread(&magic2, 4);
+
+ /* We first run through the whole image to find a [MAPR] chunk. This is needed
+ because, unfortunately, the MAPR chunk is not always the first chunk (see
+ Super 24-in-1). When such a chunk is found, we set mapr_chunk_found=1 and
+ we go back to load other chunks! */
+ if (!mapr_chunk_found)
+ {
+ if ((magic2[0] == 'M') && (magic2[1] == 'A') && (magic2[2] == 'P') && (magic2[3] == 'R'))
+ {
+ mapr_chunk_found = TRUE;
+ logerror("[MAPR] chunk found: ");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ if (chunk_length <= 0x20)
+ fread(&unif_mapr, chunk_length);
+ logerror("%s\n", unif_mapr);
+
+ /* now that we found the MAPR chunk, we can go back to load other chunks */
+ fseek(0x20, SEEK_SET);
+ read_length = 0x20;
+ }
+ else
+ {
+ logerror("Skip this chunk. We need a [MAPR] chunk before anything else.\n");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ read_length += (chunk_length + 8);
+ }
+ }
+ else
+ {
+ /* What kind of chunk do we have here? */
+ if ((magic2[0] == 'M') && (magic2[1] == 'A') && (magic2[2] == 'P') && (magic2[3] == 'R'))
+ {
+ /* The [MAPR] chunk has already been read, so we skip it */
+ /* TO DO: it would be nice to check if more than one MAPR chunk is present */
+ logerror("[MAPR] chunk found (in the 2nd run). Already loaded.\n");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'R') && (magic2[1] == 'E') && (magic2[2] == 'A') && (magic2[3] == 'D'))
+ {
+ logerror("[READ] chunk found. No support yet.\n");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'N') && (magic2[1] == 'A') && (magic2[2] == 'M') && (magic2[3] == 'E'))
+ {
+ logerror("[NAME] chunk found. No support yet.\n");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'W') && (magic2[1] == 'R') && (magic2[2] == 'T') && (magic2[3] == 'R'))
+ {
+ logerror("[WRTR] chunk found. No support yet.\n");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'T') && (magic2[1] == 'V') && (magic2[2] == 'C') && (magic2[3] == 'I'))
+ {
+ logerror("[TVCI] chunk found.\n");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ fread(&temp_byte, 1);
+ logerror("Television Standard : %s\n", (temp_byte == 0) ? "NTSC" : (temp_byte == 1) ? "PAL" : "Does not matter");
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'T') && (magic2[1] == 'V') && (magic2[2] == 'S') && (magic2[3] == 'C')) // is this the same as TVCI??
+ {
+ logerror("[TVSC] chunk found. No support yet.\n");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'D') && (magic2[1] == 'I') && (magic2[2] == 'N') && (magic2[3] == 'F'))
+ {
+ logerror("[DINF] chunk found. No support yet.\n");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'C') && (magic2[1] == 'T') && (magic2[2] == 'R') && (magic2[3] == 'L'))
+ {
+ logerror("[CTRL] chunk found. No support yet.\n");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'B') && (magic2[1] == 'A') && (magic2[2] == 'T') && (magic2[3] == 'R'))
+ {
+ logerror("[BATR] chunk found. No support yet.\n");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'V') && (magic2[1] == 'R') && (magic2[2] == 'O') && (magic2[3] == 'R'))
+ {
+ logerror("[VROR] chunk found. No support yet.\n");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'M') && (magic2[1] == 'I') && (magic2[2] == 'R') && (magic2[3] == 'R'))
+ {
+ logerror("[MIRR] chunk found.\n");
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ fread(&mirror, 1);
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'P') && (magic2[1] == 'C') && (magic2[2] == 'K'))
+ {
+ logerror("[PCK%c] chunk found. No support yet.\n", magic2[3]);
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'C') && (magic2[1] == 'C') && (magic2[2] == 'K'))
+ {
+ logerror("[CCK%c] chunk found. No support yet.\n", magic2[3]);
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'P') && (magic2[1] == 'R') && (magic2[2] == 'G'))
+ {
+ logerror("[PRG%c] chunk found. ", magic2[3]);
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+ prg_size += chunk_length;
+
+ if (chunk_length / 0x4000)
+ logerror("It consists of %d 16K-blocks.\n", chunk_length / 0x4000);
+ else
+ {
+ small_prg = TRUE;
+ logerror("This chunk is smaller than 16K: the emulation might have issues. Please report this file to the MESS forums.\n");
+ }
+
+ /* Read in the program chunks */
+ fread(&temp_prg[prg_start], chunk_length);
+
+ prg_start += chunk_length;
+ read_length += (chunk_length + 8);
+ }
+ else if ((magic2[0] == 'C') && (magic2[1] == 'H') && (magic2[2] == 'R'))
+ {
+ logerror("[CHR%c] chunk found. ", magic2[3]);
+ fread(&buffer, 4);
+ chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
+ vrom_size += chunk_length;
+
+ logerror("It consists of %d 8K-blocks.\n", chunk_length / 0x2000);
+
+ /* Read in the vrom chunks */
+ fread(&temp_chr[chr_start], chunk_length);
+
+ chr_start += chunk_length;
+ read_length += (chunk_length + 8);
+ }
+ else
+ {
+ logerror("Unsupported UNIF chunk or corrupted header. Please report the problem at MESS Board.\n");
+ read_length = size;
+ }
+ }
+ } while (size > read_length);
+
+ if (!mapr_chunk_found)
+ {
+ fatalerror("UNIF should have a [MAPR] chunk to work. Check if your image has been corrupted\n");
+ }
+
+ if (!prg_start)
+ {
+ fatalerror("No PRG found. Please report the problem at MESS Board.\n");
+ }
+
+ // SETUP step 2: getting PCB and other settings
+ int pcb_id = 0, battery = 0, prgram = 0, vram_chunks = 0;
+ unif_mapr_setup(unif_mapr, &pcb_id, &battery, &prgram, &vram_chunks);
+
+ // SETUP step 3: storing the info needed for emulation
+ m_pcb_id = pcb_id;
+ if (battery)
+ battery_size = NES_BATTERY_SIZE; // we should allow for smaller battery!
+ prgram_size = prgram * 0x2000;
+ vram_size = vram_chunks * 0x2000;
+
+ m_cart->set_four_screen_vram(FALSE);
+ switch (mirror)
+ {
+ case 0: // Horizontal Mirroring (Hard Wired)
+ m_cart->set_mirroring(PPU_MIRROR_HORZ);
+ break;
+ case 1: // Vertical Mirroring (Hard Wired)
+ m_cart->set_mirroring(PPU_MIRROR_VERT);
+ break;
+ case 2: // Mirror All Pages From $2000 (Hard Wired)
+ m_cart->set_mirroring(PPU_MIRROR_LOW);
+ break;
+ case 3: // Mirror All Pages From $2400 (Hard Wired)
+ m_cart->set_mirroring(PPU_MIRROR_HIGH);
+ break;
+ case 4: // Four Screens of VRAM (Hard Wired)
+ m_cart->set_four_screen_vram(TRUE);
+ m_cart->set_mirroring(PPU_MIRROR_4SCREEN);
+ break;
+ case 5: // Mirroring Controlled By Mapper Hardware
+ logerror("Mirroring handled by the board hardware.\n");
+ // default to horizontal at start
+ m_cart->set_mirroring(PPU_MIRROR_HORZ);
+ break;
+ default:
+ logerror("Undocumented mirroring value.\n");
+ // default to horizontal
+ m_cart->set_mirroring(PPU_MIRROR_HORZ);
+ break;
+ }
+
+ // SETUP step 4: logging what we have found
+ logerror("-- Board %s\n", unif_mapr);
+ logerror("-- PRG 0x%x (%d x 16k chunks)\n", prg_size, prg_size / 0x4000);
+ logerror("-- VROM 0x%x (%d x 8k chunks)\n", vrom_size, vrom_size / 0x2000);
+ logerror("-- VRAM 0x%x (%d x 8k chunks)\n", vram_size, vram_size / 0x2000);
+
+ // SETUP steps 5/6: allocate pointers for PRG/VROM and load the data!
+ if (prg_size == 0x4000)
+ {
+ m_cart->prg_alloc(0x8000, tag());
+ memcpy(m_cart->get_prg_base(), &temp_prg[0], 0x4000);
+ memcpy(m_cart->get_prg_base() + 0x4000, m_cart->get_prg_base(), 0x4000);
+ }
+ else
+ {
+ m_cart->prg_alloc(prg_size, tag());
+ memcpy(m_cart->get_prg_base(), &temp_prg[0], prg_size);
+ }
+
+ if (small_prg) // This is not supported yet, so warn users about this
+ osd_printf_error("Loaded UNIF file with non-16k PRG chunk. This is not supported in MESS yet.");
+
+ if (vrom_size)
+ {
+ m_cart->vrom_alloc(vrom_size, tag());
+ memcpy(m_cart->get_vrom_base(), &temp_chr[0], vrom_size);
+ }
+
+#if SPLIT_PRG
+ {
+ FILE *prgout;
+ char outname[255];
+
+ sprintf(outname, "%s.prg", filename());
+ prgout = fopen(outname, "wb");
+ if (prgout)
+ {
+ fwrite(m_cart->get_prg_base(), 1, 0x4000 * m_cart->get_prg_size(), prgout);
+ osd_printf_error("Created PRG chunk\n");
+ }
+
+ fclose(prgout);
+ }
+#endif
+
+#if SPLIT_CHR
+ if (state->m_chr_chunks > 0)
+ {
+ FILE *chrout;
+ char outname[255];
+
+ sprintf(outname, "%s.chr", filename());
+ chrout= fopen(outname, "wb");
+ if (chrout)
+ {
+ fwrite(m_cart->get_vrom_base(), 1, m_cart->get_vrom_size(), chrout);
+ osd_printf_error("Created CHR chunk\n");
+ }
+ fclose(chrout);
+ }
+#endif
+ // SETUP steps 7: allocate the remaining pointer, when needed
+ if (vram_size)
+ m_cart->vram_alloc(vram_size);
+ if (prgram_size)
+ m_cart->prgram_alloc(prgram_size);
+
+ // Attempt to load a battery file for this ROM
+ // A few boards have internal RAM with a battery (MMC6, Taito X1-005 & X1-017, etc.)
+ if (battery_size || mapper_sram_size)
+ {
+ UINT32 tot_size = battery_size + mapper_sram_size;
+ dynamic_buffer temp_nvram(tot_size);
+ battery_load(&temp_nvram[0], tot_size, 0x00);
+ if (battery_size)
+ {
+ m_cart->battery_alloc(battery_size);
+ memcpy(m_cart->get_battery_base(), &temp_nvram[0], battery_size);
+ }
+ if (mapper_sram_size)
+ memcpy(m_cart->get_mapper_sram_base(), &temp_nvram[battery_size], mapper_sram_size);
+ }
+
+ logerror("UNIF support is only very preliminary.\n");
+}
+
+const char * nes_cart_slot_device::get_default_card_unif(UINT8 *ROM, UINT32 len)
+{
+ UINT32 chunk_length = 0, read_length = 0x20;
+ int pcb_id = 0, battery = 0, prgram = 0, vram_chunks = 0;
+ char unif_mapr[32];
+
+ do
+ {
+ if ((ROM[read_length + 0] == 'M') && (ROM[read_length + 1] == 'A') && (ROM[read_length + 2] == 'P') && (ROM[read_length + 3] == 'R'))
+ {
+ chunk_length = ROM[read_length + 4] | (ROM[read_length + 5] << 8) | (ROM[read_length + 6] << 16) | (ROM[read_length + 7] << 24);
+
+ if (chunk_length <= 0x20)
+ memcpy(unif_mapr, ROM + read_length + 8, chunk_length);
+
+ read_length += (chunk_length + 8);
+ }
+ else
+ {
+ chunk_length = ROM[read_length + 4] | (ROM[read_length + 5] << 8) | (ROM[read_length + 6] << 16) | (ROM[read_length + 7] << 24);
+ read_length += (chunk_length + 8);
+ }
+ } while (len > read_length);
+
+ unif_mapr_setup(unif_mapr, &pcb_id, &battery, &prgram, &vram_chunks);
+
+ return nes_get_slot(pcb_id);
+}
diff --git a/src/devices/bus/nes/ntdec.c b/src/devices/bus/nes/ntdec.c
new file mode 100644
index 00000000000..4dd7e56a842
--- /dev/null
+++ b/src/devices/bus/nes/ntdec.c
@@ -0,0 +1,186 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for NTDEC PCBs
+
+
+ Here we emulate the following PCBs
+
+ * NTDEC ASDER [mapper 112]
+ * NTDEC Fighting Hero [mapper 193]
+
+ TODO:
+ - why is Master Shooter not working?
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "ntdec.h"
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_NTDEC_ASDER = &device_creator<nes_ntdec_asder_device>;
+const device_type NES_NTDEC_FH = &device_creator<nes_ntdec_fh_device>;
+
+
+nes_ntdec_asder_device::nes_ntdec_asder_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_NTDEC_ASDER, "NES Cart NTDEC Asder PCB", tag, owner, clock, "nes_ntdec_asder", __FILE__)
+{
+}
+
+nes_ntdec_fh_device::nes_ntdec_fh_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_NTDEC_FH, "NES Cart NTDEC Fighting Hero PCB", tag, owner, clock, "nes_ntdec_fh", __FILE__)
+{
+}
+
+
+
+
+void nes_ntdec_asder_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+}
+
+void nes_ntdec_asder_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_latch = 0;
+}
+
+void nes_ntdec_fh_device::device_start()
+{
+ common_start();
+}
+
+void nes_ntdec_fh_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32((m_prg_chunks - 1) >> 1);
+ chr8(0, m_chr_source);
+ set_nt_mirroring(PPU_MIRROR_VERT);
+}
+
+
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ NTDEC ASDER Bootleg Board
+
+ Games: Cobra Mission, Fighting Hero III, Huang Di, Master
+ Shooter
+
+ iNES: mapper 112
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_ntdec_asder_device::write_h)
+{
+ LOG_MMC(("ntdec_asder write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset)
+ {
+ case 0x0000:
+ m_latch = data & 0x07;
+ break;
+ case 0x2000:
+ switch (m_latch)
+ {
+ case 0:
+ prg8_89(data);
+ break;
+ case 1:
+ prg8_ab(data);
+ break;
+ case 2:
+ data &= 0xfe;
+ chr1_0(data, CHRROM);
+ chr1_1(data + 1, CHRROM);
+ break;
+ case 3:
+ data &= 0xfe;
+ chr1_2(data, CHRROM);
+ chr1_3(data + 1, CHRROM);
+ break;
+ case 4:
+ chr1_4(data, CHRROM);
+ break;
+ case 5:
+ chr1_5(data, CHRROM);
+ break;
+ case 6:
+ chr1_6(data, CHRROM);
+ break;
+ case 7:
+ chr1_7(data, CHRROM);
+ break;
+ }
+ break;
+ case 0x6000:
+ set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Bootleg Board by NTDEC for Fighting Hero
+
+ Games: Fighting Hero
+
+ Very simple mapper: writes to 0x6000-0x7fff swap PRG and
+ CHR banks.
+
+ iNES: mapper 193
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_ntdec_fh_device::write_m)
+{
+ LOG_MMC(("ntdec_fh write_m, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x03)
+ {
+ case 0:
+ chr4_0(data >> 2, CHRROM);
+ break;
+ case 1:
+ chr2_4(data >> 1, CHRROM);
+ break;
+ case 2:
+ chr2_6(data >> 1 , CHRROM);
+ break;
+ case 3:
+ prg8_89(data);
+ break;
+ }
+}
diff --git a/src/devices/bus/nes/ntdec.h b/src/devices/bus/nes/ntdec.h
new file mode 100644
index 00000000000..00712eecf1c
--- /dev/null
+++ b/src/devices/bus/nes/ntdec.h
@@ -0,0 +1,51 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_NTDEC_H
+#define __NES_NTDEC_H
+
+#include "nxrom.h"
+
+
+// ======================> nes_ntdec_asder_device
+
+class nes_ntdec_asder_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_ntdec_asder_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+};
+
+
+// ======================> nes_ntdec_fh_device
+
+class nes_ntdec_fh_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_ntdec_fh_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+
+ virtual void pcb_reset();
+};
+
+
+
+
+
+// device type definition
+extern const device_type NES_NTDEC_ASDER;
+extern const device_type NES_NTDEC_FH;
+
+#endif
diff --git a/src/devices/bus/nes/nxrom.c b/src/devices/bus/nes/nxrom.c
new file mode 100644
index 00000000000..431d384e7f1
--- /dev/null
+++ b/src/devices/bus/nes/nxrom.c
@@ -0,0 +1,583 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for Nintendo xxROM PCBs
+
+
+ Here we emulate the following PCBs
+
+ * Nintendo NROM [mapper 0]
+ * Nintendo AxROM [mapper 7]
+ * Nintendo BxROM [mapper 34]
+ * Nintendo CNROM [mapper 3 & 185]
+ * Nintendo CPROM [mapper 13]
+ * Nintendo GxROM/MxROM [mapper 66]
+ * Nintendo UxROM [mapper 2] + Crazy Climbers variant [mapper 180]
+ * Nintendo UN1ROM [mapper 94]
+
+ Known issues on specific mappers:
+
+ * 000 F1 Race requires more precise PPU timing. It currently has plenty of 1-line glitches.
+ * 003 Firehouse Rescue has flashing graphics (same PPU issue as Back to the Future 2 & 3?)
+ * 007 Marble Madness has small graphics corruptions
+ * 034 Titanic 1912 (pirate BxROM) has missing gfx (same PPU issue of many Waixing titles almost for sure)
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "nxrom.h"
+
+#include "sound/samples.h"
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_NROM = &device_creator<nes_nrom_device>;
+const device_type NES_NROM368 = &device_creator<nes_nrom368_device>;
+const device_type NES_FCBASIC = &device_creator<nes_fcbasic_device>;
+const device_type NES_AXROM = &device_creator<nes_axrom_device>;
+const device_type NES_BXROM = &device_creator<nes_bxrom_device>;
+const device_type NES_CNROM = &device_creator<nes_cnrom_device>;
+const device_type NES_CPROM = &device_creator<nes_cprom_device>;
+const device_type NES_GXROM = &device_creator<nes_gxrom_device>;
+const device_type NES_UXROM = &device_creator<nes_uxrom_device>;
+const device_type NES_UXROM_CC = &device_creator<nes_uxrom_cc_device>;
+const device_type NES_UN1ROM = &device_creator<nes_un1rom_device>;
+const device_type NES_NOCHR = &device_creator<nes_nochr_device>;
+
+
+nes_nrom_device::nes_nrom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_nes_cart_interface( mconfig, *this )
+{
+}
+
+nes_nrom_device::nes_nrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, NES_NROM, "NES Cart NROM PCB", tag, owner, clock, "nes_nrom", __FILE__),
+ device_nes_cart_interface( mconfig, *this )
+{
+}
+
+nes_nrom368_device::nes_nrom368_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_NROM368, "NES Cart NROM-368 PCB", tag, owner, clock, "nes_nrom368", __FILE__)
+{
+}
+
+nes_fcbasic_device::nes_fcbasic_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_FCBASIC, "NES Cart Famicom BASIC PCB", tag, owner, clock, "nes_fcbasic", __FILE__)
+{
+}
+
+nes_axrom_device::nes_axrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_AXROM, "NES Cart AxROM PCB", tag, owner, clock, "nes_axrom", __FILE__)
+{
+}
+
+nes_bxrom_device::nes_bxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_BXROM, "NES Cart BxROM PCB", tag, owner, clock, "nes_bxrom", __FILE__)
+{
+}
+
+nes_cnrom_device::nes_cnrom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : nes_nrom_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+nes_cnrom_device::nes_cnrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_CNROM, "NES Cart CNROM PCB", tag, owner, clock, "nes_cnrom", __FILE__)
+{
+}
+
+nes_cprom_device::nes_cprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_CPROM, "NES Cart CPROM PCB", tag, owner, clock, "nes_cprom", __FILE__)
+{
+}
+
+nes_gxrom_device::nes_gxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_GXROM, "NES Cart GxROM PCB", tag, owner, clock, "nes_gxrom", __FILE__)
+{
+}
+
+nes_uxrom_device::nes_uxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_UXROM, "NES Cart UxROM PCB", tag, owner, clock, "nes_uxrom", __FILE__)
+{
+}
+
+nes_uxrom_cc_device::nes_uxrom_cc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_UXROM_CC, "NES Cart UNROM M5 PCB (Crazy Climber)", tag, owner, clock, "nes_uxrom_cc", __FILE__)
+{
+}
+
+nes_un1rom_device::nes_un1rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_UN1ROM, "NES Cart UN1ROM PCB", tag, owner, clock, "nes_un1rom", __FILE__)
+{
+}
+
+nes_nochr_device::nes_nochr_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_NOCHR, "NES Cart NoCash NOCHR PCB", tag, owner, clock, "nes_nochr", __FILE__)
+{
+}
+
+
+void nes_nrom_device::common_start()
+{
+ // PRG
+ save_item(NAME(m_prg_bank));
+
+ // CHR
+ save_item(NAME(m_chr_source));
+ save_item(NAME(m_chr_src));
+ save_item(NAME(m_chr_orig));
+
+ // NT
+ save_item(NAME(m_mirroring));
+ save_item(NAME(m_nt_src));
+ save_item(NAME(m_nt_orig));
+ save_item(NAME(m_nt_writable));
+
+ // open bus
+ save_item(NAME(m_open_bus));
+}
+
+void nes_nrom_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_axrom_device::device_start()
+{
+ common_start();
+}
+
+void nes_axrom_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+
+ set_nt_mirroring(PPU_MIRROR_LOW);
+}
+
+void nes_bxrom_device::device_start()
+{
+ common_start();
+}
+
+void nes_bxrom_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_cnrom_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_chr_open_bus));
+}
+
+void nes_cnrom_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+
+ m_chr_open_bus = 0;
+}
+
+void nes_cprom_device::device_start()
+{
+ common_start();
+}
+
+void nes_cprom_device::pcb_reset()
+{
+ m_chr_source = CHRRAM;
+ prg32(0);
+ chr4_0(0, m_chr_source);
+ chr4_4(0, m_chr_source);
+}
+
+void nes_gxrom_device::device_start()
+{
+ common_start();
+}
+
+void nes_gxrom_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_uxrom_device::device_start()
+{
+ common_start();
+}
+
+void nes_uxrom_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+}
+
+void nes_uxrom_cc_device::device_start()
+{
+ common_start();
+}
+
+void nes_uxrom_cc_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_un1rom_device::device_start()
+{
+ common_start();
+}
+
+void nes_un1rom_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+}
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ NROM board emulation
+
+ Games: Mario Bros., Super Mario Bros., Tennis and most of
+ the first generation games
+
+ iNES: mapper 0
+
+ In MESS: Supported, no need of specific handlers or IRQ
+
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ NROM-368 board emulation
+
+ iNES: mapper 0 with 3xPRG banks
+ This is an homebrew extension to map linearly 46KB
+ or PRG in boards with no PRG bankswitch logic
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+READ8_MEMBER(nes_nrom368_device::read_l)
+{
+ LOG_MMC(("nrom368 read_l, offset: %04x\n", offset));
+ offset += 0x100;
+ if (offset >= 0x800)
+ return m_prg[offset - 0x800];
+ else
+ return m_open_bus;
+}
+
+READ8_MEMBER(nes_nrom368_device::read_m)
+{
+ LOG_MMC(("nrom368 read_m, offset: %04x\n", offset));
+ return m_prg[0x1800 + (offset & 0x1fff)];
+}
+
+READ8_MEMBER(nes_nrom368_device::read_h)
+{
+ LOG_MMC(("nrom368 read_h, offset: %04x\n", offset));
+ return m_prg[0x3800 + (offset & 0x7fff)];
+}
+
+/*-------------------------------------------------
+
+ AxROM board emulation
+
+ Games: Arch Rivals, Battletoads, Cabal, Commando, Solstice
+
+ writes to 0x8000-0xffff change PRG banks + sets mirroring
+
+ AMROM has bus conflict
+ AOROM has generally bus conflict too, but some later pcbs
+ added some discrete component to disable ROM and avoid
+ conflicts. No AOROM games is known to glitch due to lack of
+ bus conflict, so it seems safe to emulate AOROM
+ without bus conflict.
+
+ iNES: mapper 7
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_axrom_device::write_h)
+{
+ LOG_MMC(("axrom write_h, offset: %04x, data: %02x\n", offset, data));
+
+ // this pcb is subject to bus conflict
+ data = account_bus_conflict(offset, data);
+
+ set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW);
+ prg32(data);
+}
+
+/*-------------------------------------------------
+
+ BxROM board emulation
+
+ writes to 0x8000-0xffff change PRG banks
+
+ iNES: mapper 34
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bxrom_device::write_h)
+{
+ /* This portion of the mapper is nearly identical to Mapper 7, except no one-screen mirroring */
+ /* Deadly Towers is really a BxROM game - the demo screens look wrong using mapper 7. */
+ LOG_MMC(("bxrom write_h, offset: %04x, data: %02x\n", offset, data));
+
+ // this pcb is subject to bus conflict, but the same is not true for some pirate variants
+ data = account_bus_conflict(offset, data);
+
+ prg32(data);
+}
+
+/*-------------------------------------------------
+
+ CNROM board emulation
+
+ Games: B-Wings, Mighty Bomb Jack, Seicross, Spy vs. Spy,
+ Adventure Island, Flipull, Friday 13th, GeGeGe no
+ Kitarou, Ghostbusters, Gradius, Hokuto no Ken, Milon's
+ Secret Castle
+
+ writes to 0x8000-0xffff change CHR 8K banks
+
+ missing BC?
+
+ iNES: mappers 3 & 185 (the latter for games using Pins as
+ protection)
+
+ Notice that BANDAI_PT554 board (Aerobics Studio) uses very
+ similar hardware but with an additional sound chip which
+ gets writes to 0x6000 (currently unemulated in MESS)
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_cnrom_device::write_h)
+{
+ LOG_MMC(("cxrom write_h, offset: %04x, data: %02x\n", offset, data));
+
+ // this pcb is subject to bus conflict, but the same is not true for some pirate variants
+ data = account_bus_conflict(offset, data);
+
+ if (m_ce_mask)
+ {
+ //printf("mask %x state %x\n", m_ce_mask, m_ce_state);
+ chr8(data & ~m_ce_mask, CHRROM);
+
+ if ((data & m_ce_mask) == m_ce_state)
+ m_chr_open_bus = 0;
+ else
+ m_chr_open_bus = 1;
+ }
+ else
+ chr8(data, CHRROM);
+}
+
+READ8_MEMBER(nes_cnrom_device::chr_r)
+{
+ int bank = offset >> 10;
+
+ // a few CNROM boards contained copy protection schemes through
+ // suitably configured diodes, so that subsequent CHR reads can
+ // give actual VROM content or open bus values.
+ // For most boards, chr_open_bus remains always zero.
+ if (m_chr_open_bus)
+ return m_open_bus;
+
+ return m_chr_access[bank][offset & 0x3ff];
+}
+
+
+/*-------------------------------------------------
+
+ CPROM board emulation
+
+ Games: Videomation
+
+ writes to 0x8000-0xffff change CHR 4K lower banks
+
+ iNES: mapper 13
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_cprom_device::write_h)
+{
+ LOG_MMC(("cprom write_h, offset: %04x, data: %02x\n", offset, data));
+
+ // this pcb is subject to bus conflict
+ data = account_bus_conflict(offset, data);
+
+ chr4_4(data, CHRRAM);
+}
+
+
+/*-------------------------------------------------
+
+ GxROM/MxROM board emulation
+
+ writes to 0x8000-0xffff change PRG and CHR banks
+
+ iNES: mapper 66
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_gxrom_device::write_h)
+{
+ LOG_MMC(("gxrom write_h, offset %04x, data: %02x\n", offset, data));
+
+ // this pcb is subject to bus conflict
+ data = account_bus_conflict(offset, data);
+
+ prg32((data & 0xf0) >> 4);
+ chr8(data & 0x0f, CHRROM);
+}
+
+/*-------------------------------------------------
+
+ UxROM board emulation
+
+ Games: Castlevania, Dragon Quest II, Duck Tales, MegaMan,
+ Metal Gear
+
+ writes to 0x8000-0xffff change PRG 16K lower banks
+
+ iNES: mapper 2
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_uxrom_device::write_h)
+{
+ LOG_MMC(("uxrom write_h, offset: %04x, data: %02x\n", offset, data));
+
+ // this pcb is subject to bus conflict
+ data = account_bus_conflict(offset, data);
+
+ prg16_89ab(data);
+}
+
+/*-------------------------------------------------
+
+ Nihon Bussan UNROM M5
+
+ Games: Crazy Climber Jpn
+
+ Very simple mapper: prg16_89ab is always set to bank 0,
+ while prg16_cdef is set by writes to 0x8000-0xffff. The game
+ uses a custom controller.
+
+ iNES: mapper 180
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_uxrom_cc_device::write_h)
+{
+ LOG_MMC(("uxrom_cc write_h, offset: %04x, data: %02x\n", offset, data));
+
+ prg16_cdef(data);
+}
+
+/*-------------------------------------------------
+
+ UN1ROM board emulation
+
+ Games: Senjou no Okami
+
+ writes to 0x8000-0xffff change PRG 16K lower banks
+
+ iNES: mapper 94
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_un1rom_device::write_h)
+{
+ LOG_MMC(("un1rom write_h, offset: %04x, data: %02x\n", offset, data));
+
+ // this pcb is subject to bus conflict
+ data = account_bus_conflict(offset, data);
+
+ prg16_89ab(data >> 2);
+}
+
+/*-------------------------------------------------
+
+ NoCash NOCHR board emulation
+
+ This is an homebrew PCB design on a single chip
+ (+possibly CIC) which uses the NTRAM as CHRRAM!
+
+ iNES: mapper 218
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_nochr_device::chr_w)
+{
+ int mirr = get_mirroring();
+ if (mirr == PPU_MIRROR_HIGH)
+ m_ciram[(offset & 0x3ff) + 0x000] = data;
+ else if (mirr == PPU_MIRROR_LOW)
+ m_ciram[(offset & 0x3ff) + 0x400] = data;
+ else
+ m_ciram[offset & 0x7ff] = data; // not sure here, since there is no software to test...
+}
+
+READ8_MEMBER(nes_nochr_device::chr_r)
+{
+ int mirr = get_mirroring();
+ if (mirr == PPU_MIRROR_HIGH)
+ return m_ciram[(offset & 0x3ff) + 0x000];
+ else if (mirr == PPU_MIRROR_LOW)
+ return m_ciram[(offset & 0x3ff) + 0x400];
+ else
+ return m_ciram[offset & 0x7ff]; // not sure here, since there is no software to test...
+}
diff --git a/src/devices/bus/nes/nxrom.h b/src/devices/bus/nes/nxrom.h
new file mode 100644
index 00000000000..fe105be0189
--- /dev/null
+++ b/src/devices/bus/nes/nxrom.h
@@ -0,0 +1,216 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_MMCX_H
+#define __NES_MMCX_H
+
+#include "nes_slot.h"
+#include "sound/samples.h"
+
+
+// ======================> nes_nrom_device
+
+class nes_nrom_device : public device_t,
+ public device_nes_cart_interface
+{
+public:
+ // construction/destruction
+ nes_nrom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_nrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual void device_start() { common_start(); }
+
+ virtual void pcb_reset();
+
+ void common_start();
+};
+
+
+// ======================> nes_nrom368_device
+
+class nes_nrom368_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_nrom368_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual DECLARE_READ8_MEMBER(read_l);
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_READ8_MEMBER(read_h);
+};
+
+
+// ======================> nes_fcbasic_device
+
+class nes_fcbasic_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_fcbasic_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // emulate the additional WRAM
+};
+
+
+// ======================> nes_axrom_device
+
+class nes_axrom_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_axrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_bxrom_device
+
+class nes_bxrom_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_bxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_cnrom_device
+
+class nes_cnrom_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_cnrom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_cnrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(chr_r);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_chr_open_bus;
+};
+
+
+// ======================> nes_cprom_device
+
+class nes_cprom_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_cprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_gxrom_device
+
+class nes_gxrom_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_gxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_uxrom_device
+
+class nes_uxrom_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_uxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_uxrom_cc_device
+
+class nes_uxrom_cc_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_uxrom_cc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_un1rom_device
+
+class nes_un1rom_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_un1rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_nochr_device
+
+class nes_nochr_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_nochr_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual DECLARE_READ8_MEMBER(chr_r);
+ virtual DECLARE_WRITE8_MEMBER(chr_w);
+};
+
+
+
+// device type definition
+extern const device_type NES_NROM;
+extern const device_type NES_NROM368;
+extern const device_type NES_FCBASIC;
+extern const device_type NES_AXROM;
+extern const device_type NES_BXROM;
+extern const device_type NES_CNROM;
+extern const device_type NES_CPROM;
+extern const device_type NES_GXROM;
+extern const device_type NES_UXROM;
+extern const device_type NES_UXROM_CC;
+extern const device_type NES_UN1ROM;
+extern const device_type NES_NOCHR;
+
+#endif
diff --git a/src/devices/bus/nes/pirate.c b/src/devices/bus/nes/pirate.c
new file mode 100644
index 00000000000..557831ed144
--- /dev/null
+++ b/src/devices/bus/nes/pirate.c
@@ -0,0 +1,1387 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for pirate cart PCBs
+
+
+ Here we emulate the various PCBs used by Asian & Korean pirate games
+
+ TODO:
+ - Are the scrolling glitches (check status bar) in Magic Dragon correct? FWIW, NEStopia behaves similarly
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "pirate.h"
+
+#include "cpu/m6502/m6502.h"
+#include "video/ppu2c0x.h" // this has to be included so that IRQ functions can access PPU_BOTTOM_VISIBLE_SCANLINE
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_AGCI_50282 = &device_creator<nes_agci_device>;
+const device_type NES_DREAMTECH01 = &device_creator<nes_dreamtech_device>;
+const device_type NES_FUKUTAKE = &device_creator<nes_fukutake_device>;
+const device_type NES_FUTUREMEDIA = &device_creator<nes_futuremedia_device>;
+const device_type NES_MAGSERIES = &device_creator<nes_magseries_device>;
+const device_type NES_DAOU306 = &device_creator<nes_daou306_device>;
+const device_type NES_SUBOR0 = &device_creator<nes_subor0_device>;
+const device_type NES_SUBOR1 = &device_creator<nes_subor1_device>;
+const device_type NES_CC21 = &device_creator<nes_cc21_device>;
+const device_type NES_XIAOZY = &device_creator<nes_xiaozy_device>;
+const device_type NES_EDU2K = &device_creator<nes_edu2k_device>;
+const device_type NES_T230 = &device_creator<nes_t230_device>;
+const device_type NES_MK2 = &device_creator<nes_mk2_device>;
+const device_type NES_WHERO = &device_creator<nes_whero_device>;
+const device_type NES_43272 = &device_creator<nes_43272_device>;
+const device_type NES_TF1201 = &device_creator<nes_tf1201_device>;
+const device_type NES_CITYFIGHT = &device_creator<nes_cityfight_device>;
+
+
+nes_agci_device::nes_agci_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_AGCI_50282, "NES Cart AGCI 50282 PCB", tag, owner, clock, "nes_agci50282", __FILE__)
+{
+}
+
+nes_dreamtech_device::nes_dreamtech_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_DREAMTECH01, "NES Cart Dreamtech01 PCB", tag, owner, clock, "nes_dreamtech", __FILE__)
+{
+}
+
+nes_fukutake_device::nes_fukutake_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_FUKUTAKE, "NES Cart Fukutake Study Box PCB", tag, owner, clock, "nes_fukutake", __FILE__)
+{
+}
+
+nes_futuremedia_device::nes_futuremedia_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_FUTUREMEDIA, "NES Cart FutureMedia PCB", tag, owner, clock, "nes_futuremedia", __FILE__)
+{
+}
+
+nes_magseries_device::nes_magseries_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_MAGSERIES, "NES Cart Magical Series PCB", tag, owner, clock, "nes_magseries", __FILE__)
+{
+}
+
+nes_daou306_device::nes_daou306_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_DAOU306, "NES Cart Daou 306 PCB", tag, owner, clock, "nes_daou306", __FILE__)
+{
+}
+
+nes_subor0_device::nes_subor0_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SUBOR0, "NES Cart Subor Type 0 PCB", tag, owner, clock, "nes_subor0", __FILE__)
+{
+}
+
+nes_subor1_device::nes_subor1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SUBOR1, "NES Cart Subor Type 1 PCB", tag, owner, clock, "nes_subor1", __FILE__)
+{
+}
+
+nes_cc21_device::nes_cc21_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_CC21, "NES Cart CC-21 PCB", tag, owner, clock, "nes_cc21", __FILE__)
+{
+}
+
+nes_xiaozy_device::nes_xiaozy_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_XIAOZY, "NES Cart Xiao Zhuan Yuan PCB", tag, owner, clock, "nes_xiaozy", __FILE__)
+{
+}
+
+nes_edu2k_device::nes_edu2k_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_EDU2K, "NES Cart Educational Computer 2000 PCB", tag, owner, clock, "nes_edu2k", __FILE__)
+{
+}
+
+nes_t230_device::nes_t230_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_T230, "NES Cart T-230 PCB", tag, owner, clock, "nes_t230", __FILE__)
+{
+}
+
+nes_mk2_device::nes_mk2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_MK2, "NES Cart Mortal Kombat 2 PCB", tag, owner, clock, "nes_mk2", __FILE__)
+{
+}
+
+nes_whero_device::nes_whero_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_WHERO, "NES Cart World Heroes PCB", tag, owner, clock, "nes_whero", __FILE__)
+{
+}
+
+nes_43272_device::nes_43272_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_43272, "NES Cart UNL-43272 PCB", tag, owner, clock, "nes_43272", __FILE__)
+{
+}
+
+nes_tf1201_device::nes_tf1201_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_TF1201, "NES Cart UNL-TF1201 PCB", tag, owner, clock, "nes_tf1201", __FILE__)
+{
+}
+
+nes_cityfight_device::nes_cityfight_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_TF1201, "NES Cart City Fighter PCB", tag, owner, clock, "nes_cityfight", __FILE__)
+{
+}
+
+
+
+
+void nes_agci_device::device_start()
+{
+ common_start();
+}
+
+void nes_agci_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_dreamtech_device::device_start()
+{
+ common_start();
+}
+
+void nes_dreamtech_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(8);
+ chr8(0, m_chr_source);
+}
+
+void nes_fukutake_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+
+ // 2816 (?) bytes of RAM
+ save_item(NAME(m_ram));
+}
+
+void nes_fukutake_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(0);
+ chr8(0, m_chr_source);
+
+ m_latch = 0;
+}
+
+void nes_futuremedia_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_irq_clear));
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_irq_count_latch));
+}
+
+void nes_futuremedia_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_irq_clear = 0;
+ m_irq_enable = 0;
+ m_irq_count = m_irq_count_latch = 0;
+}
+
+void nes_magseries_device::device_start()
+{
+ common_start();
+}
+
+void nes_magseries_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_daou306_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_reg));
+}
+
+void nes_daou306_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(m_prg_chunks - 2);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+ set_nt_mirroring(PPU_MIRROR_LOW);
+
+ memset(m_reg, 0, sizeof(m_reg));
+}
+
+void nes_subor0_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_reg));
+}
+
+void nes_subor0_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(0x20);
+ chr8(0, m_chr_source);
+
+ memset(m_reg, 0, sizeof(m_reg));
+}
+
+void nes_subor1_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_reg));
+}
+
+void nes_subor1_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(0x07);
+ chr8(0, m_chr_source);
+
+ memset(m_reg, 0, sizeof(m_reg));
+}
+
+void nes_cc21_device::device_start()
+{
+ common_start();
+}
+
+void nes_cc21_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_xiaozy_device::device_start()
+{
+ common_start();
+}
+
+void nes_xiaozy_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32((m_prg_chunks - 1) >> 1);
+ chr8(0, m_chr_source);
+}
+
+void nes_edu2k_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+}
+
+void nes_edu2k_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+
+ m_latch = 0;
+}
+
+void nes_t230_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_irq_mode));
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_enable_latch));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_irq_count_latch));
+ save_item(NAME(m_mmc_vrom_bank));
+}
+
+void nes_t230_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_irq_mode = 0;
+ m_irq_enable = m_irq_enable_latch = 0;
+ m_irq_count = m_irq_count_latch = 0;
+
+ memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank));
+}
+
+void nes_mk2_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_irq_clear));
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_irq_count_latch));
+}
+
+void nes_mk2_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(m_prg_chunks - 1);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+ set_nt_mirroring(PPU_MIRROR_VERT);
+
+ m_irq_clear = 0;
+ m_irq_enable = 0;
+ m_irq_count = m_irq_count_latch = 0;
+}
+
+
+void nes_whero_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_enable_latch));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_irq_count_latch));
+ save_item(NAME(m_reg));
+ save_item(NAME(m_mmc_vrom_bank));
+}
+
+void nes_whero_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0x100, m_chr_source);
+
+ m_irq_enable = 0;
+ m_irq_enable_latch = 0;
+ m_irq_count = 0;
+ m_irq_count_latch = 0;
+
+ m_reg = 0;
+ memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank));
+}
+
+void nes_43272_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+}
+
+void nes_43272_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32((m_prg_chunks - 1) >> 1);
+ chr8(0, m_chr_source);
+
+ m_latch = 0x81;
+}
+
+void nes_tf1201_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_prg));
+ save_item(NAME(m_swap));
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_enable_latch));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_mmc_vrom_bank));
+}
+
+void nes_tf1201_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank));
+ m_prg = 0;
+ m_swap = 0;
+ m_irq_enable = 0;
+ m_irq_enable_latch = 0;
+ m_irq_count = 0;
+}
+
+void nes_cityfight_device::device_start()
+{
+ common_start();
+ irq_timer = timer_alloc(TIMER_IRQ);
+ irq_timer->adjust(attotime::zero, 0, machine().device<cpu_device>("maincpu")->cycles_to_attotime(1));
+
+ save_item(NAME(m_prg_reg));
+ save_item(NAME(m_prg_mode));
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_mmc_vrom_bank));
+}
+
+void nes_cityfight_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank));
+ m_prg_reg = 0;
+ m_prg_mode = 0;
+ m_irq_enable = 0;
+ m_irq_count = 0;
+}
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ AGCI 50282 bootleg board emulation
+
+ Games: Death Race
+
+ iNES: mapper 144
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_agci_device::write_h)
+{
+ LOG_MMC(("agci write_h, offset: %04x, data: %02x\n", offset, data));
+
+ // this pcb is subject to bus conflict
+ UINT8 temp = account_bus_conflict(offset, 0xff);
+ data = (data & temp) | (temp & 1);
+
+ chr8(data >> 4, CHRROM);
+ prg32(data);
+}
+
+/*-------------------------------------------------
+
+ Board DREAMTECH01
+
+ Games: Korean Igo
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_dreamtech_device::write_l)
+{
+ LOG_MMC(("dreamtech write_l, offset: %04x, data: %02x\n", offset, data));
+ offset += 0x100;
+
+ if (offset == 0x1020) /* 0x5020 */
+ prg16_89ab(data);
+}
+
+/*-------------------------------------------------
+
+ Bootleg Board by Fukutake
+
+ Games: Study Box
+
+ iNES: mapper 186
+
+ In MESS: Unsupported.
+
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_fukutake_device::write_l)
+{
+ LOG_MMC(("fukutake write_l, offset: %04x, data: %02x\n", offset, data));
+ offset += 0x100;
+
+ if (offset >= 0x200 && offset < 0x400)
+ {
+ if (offset & 1)
+ prg16_89ab(data);
+ else
+ m_latch = data >> 6;
+ }
+ else if (offset >= 0x400 && offset < 0xf00)
+ m_ram[offset - 0x400] = data;
+}
+
+READ8_MEMBER(nes_fukutake_device::read_l)
+{
+ LOG_MMC(("fukutake read_l, offset: %04x\n", offset));
+ offset += 0x100;
+
+ if (offset >= 0x200 && offset < 0x400)
+ {
+ if (offset == 0x200 || offset == 0x201 || offset == 0x203)
+ return 0x00;
+ else if (offset == 0x202)
+ return 0x40;
+ else
+ return 0xff;
+ }
+ else if (offset >= 0x400 && offset < 0xf00)
+ return m_ram[offset - 0x400];
+
+ return 0;
+}
+
+WRITE8_MEMBER(nes_fukutake_device::write_m)
+{
+ LOG_MMC(("fukutake write_m, offset: %04x, data: %02x\n", offset, data));
+ m_prgram[((m_latch * 0x2000) + offset) & (m_prgram.size() - 1)] = data;
+}
+
+READ8_MEMBER(nes_fukutake_device::read_m)
+{
+ LOG_MMC(("fukutake read_m, offset: %04x\n", offset));
+ return m_prgram[((m_latch * 0x2000) + offset) & (m_prgram.size() - 1)];
+}
+
+/*-------------------------------------------------
+
+ Bootleg Board by Future Media
+
+ Games: Crayon Shin-chan (C), San Guo Zhi 4 - Chi Bi Feng Yun
+
+ iNES: mapper 117
+
+ In MESS: Unsupported.
+
+ -------------------------------------------------*/
+
+void nes_futuremedia_device::hblank_irq(int scanline, int vblank, int blanked)
+{
+ // if (scanline < PPU_BOTTOM_VISIBLE_SCANLINE)
+ {
+ if (m_irq_enable && m_irq_count)
+ {
+ m_irq_count--;
+ if (!m_irq_count)
+ m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE);
+ }
+ }
+}
+
+WRITE8_MEMBER(nes_futuremedia_device::write_h)
+{
+ LOG_MMC(("futuremedia write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset)
+ {
+ case 0x0000:
+ prg8_89(data);
+ break;
+ case 0x0001:
+ prg8_ab(data);
+ break;
+ case 0x0002:
+ prg8_cd(data);
+ break;
+ case 0x0003:
+ prg8_ef(data);
+ break;
+ case 0x2000:
+ case 0x2001:
+ case 0x2002:
+ case 0x2003:
+ case 0x2004:
+ case 0x2005:
+ case 0x2006:
+ case 0x2007:
+ chr1_x(offset & 0x07, data, CHRROM);
+ break;
+
+ case 0x5000:
+ set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ break;
+
+ case 0x4001:
+ m_irq_count_latch = data;
+ break;
+ case 0x4002:
+ // IRQ cleared
+ break;
+ case 0x4003:
+ m_irq_count = m_irq_count_latch;
+ break;
+ case 0x6000:
+ m_irq_enable = data & 0x01;
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Bootleg Board by Magic Series
+
+ Games: Magic Dragon
+
+ Very simple mapper: writes to 0x8000-0xffff set prg32 and chr8
+ banks
+
+ iNES: mapper 107
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_magseries_device::write_h)
+{
+ LOG_MMC(("magseries write_h, offset: %04x, data: %02x\n", offset, data));
+
+ prg32(data >> 1);
+ chr8(data, CHRROM);
+}
+
+/*-------------------------------------------------
+
+ Open Corp DAOU306 board
+
+ Games: Metal Force (K)
+
+ iNES: mapper 156
+
+ In MESS: Supported.
+
+ Notes: Metal Force and Buzz & Waldog only use the first 4
+ regs and no mirroring. Janggun ui Adeul uses all features
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_daou306_device::write_h)
+{
+ LOG_MMC(("daou306 write_h, offset: %04x, data: %02x\n", offset, data));
+ int reg = BIT(offset, 2) ? 8 : 0;
+
+ switch (offset)
+ {
+ case 0x4000:
+ case 0x4004:
+ m_reg[reg + 0] = data;
+ chr1_0(m_reg[0] | (m_reg[8] << 8), CHRROM);
+ break;
+ case 0x4001:
+ case 0x4005:
+ m_reg[reg + 1] = data;
+ chr1_1(m_reg[1] | (m_reg[9] << 8), CHRROM);
+ break;
+ case 0x4002:
+ case 0x4006:
+ m_reg[reg + 2] = data;
+ chr1_2(m_reg[2] | (m_reg[10] << 8), CHRROM);
+ break;
+ case 0x4003:
+ case 0x4007:
+ m_reg[reg + 3] = data;
+ chr1_3(m_reg[3] | (m_reg[11] << 8), CHRROM);
+ break;
+ case 0x4008:
+ case 0x400c:
+ m_reg[reg + 4] = data;
+ chr1_4(m_reg[4] | (m_reg[12] << 8), CHRROM);
+ break;
+ case 0x4009:
+ case 0x400d:
+ m_reg[reg + 5] = data;
+ chr1_5(m_reg[5] | (m_reg[13] << 8), CHRROM);
+ break;
+ case 0x400a:
+ case 0x400e:
+ m_reg[reg + 6] = data;
+ chr1_6(m_reg[6] | (m_reg[14] << 8), CHRROM);
+ break;
+ case 0x400b:
+ case 0x400f:
+ m_reg[reg + 7] = data;
+ chr1_7(m_reg[7] | (m_reg[15] << 8), CHRROM);
+ break;
+ case 0x4010:
+ prg16_89ab(data);
+ break;
+ case 0x4014:
+ if (data & 1)
+ set_nt_mirroring(PPU_MIRROR_HORZ);
+ else
+ set_nt_mirroring(PPU_MIRROR_VERT);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Subor bootleg board Type 0
+
+ iNES: mapper 167
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_subor0_device::write_h)
+{
+ UINT8 subor_helper1, subor_helper2;
+ LOG_MMC(("subor0 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ m_reg[(offset >> 13) & 0x03] = data;
+ subor_helper1 = ((m_reg[0] ^ m_reg[1]) << 1) & 0x20;
+ subor_helper2 = ((m_reg[2] ^ m_reg[3]) << 0) & 0x1f;
+
+ if (m_reg[1] & 0x08)
+ {
+ subor_helper1 += subor_helper2 & 0xfe;
+ subor_helper2 = subor_helper1;
+ subor_helper1 += 1;
+ }
+ else if (m_reg[1] & 0x04)
+ {
+ subor_helper2 += subor_helper1;
+ subor_helper1 = 0x1f;
+ }
+ else
+ {
+ subor_helper1 += subor_helper2;
+ subor_helper2 = 0x20;
+ }
+
+ prg16_89ab(subor_helper1);
+ prg16_cdef(subor_helper2);
+}
+
+/*-------------------------------------------------
+
+ Subor bootleg board Type 1
+
+ iNES: mapper 166
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_subor1_device::write_h)
+{
+ UINT8 subor_helper1, subor_helper2;
+ LOG_MMC(("subor1 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ m_reg[(offset >> 13) & 0x03] = data;
+ subor_helper1 = ((m_reg[0] ^ m_reg[1]) << 1) & 0x20;
+ subor_helper2 = ((m_reg[2] ^ m_reg[3]) << 0) & 0x1f;
+
+ if (m_reg[1] & 0x08)
+ {
+ subor_helper1 += subor_helper2 & 0xfe;
+ subor_helper2 = subor_helper1;
+ subor_helper2 += 1;
+ }
+ else if (m_reg[1] & 0x04)
+ {
+ subor_helper2 += subor_helper1;
+ subor_helper1 = 0x1f;
+ }
+ else
+ {
+ subor_helper1 += subor_helper2;
+ subor_helper2 = 0x07;
+ }
+
+ prg16_89ab(subor_helper1);
+ prg16_cdef(subor_helper2);
+}
+
+/*-------------------------------------------------
+
+ Board UNL-CC-21
+
+ Games: Mi Hun Che
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_cc21_device::write_h)
+{
+ LOG_MMC(("cc21 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ set_nt_mirroring(BIT(offset, 1) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW);
+ chr8((offset & 0x01), CHRROM);
+}
+
+/*-------------------------------------------------
+
+ Bootleg Board for Xiao Zhuan Yuan
+
+ Games: Shu Qi Yu - Zhi Li Xiao Zhuan Yuan
+
+ Very simple mapper: writes to 0x5ff1 set prg32 (to data>>1),
+ while writes to 0x5ff2 set chr8
+
+ iNES: mapper 176
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_xiaozy_device::write_l)
+{
+ LOG_MMC(("xiaozy write_l, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset)
+ {
+ case 0x1ef1: /* 0x5ff1 */
+ prg32(data >> 1);
+ break;
+ case 0x1ef2: /* 0x5ff2 */
+ chr8(data, CHRROM);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ UNL-EDU2000
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_edu2k_device::write_h)
+{
+ LOG_MMC(("edu2k write_h, offset: %04x, data: %02x\n", offset, data));
+
+ prg32(data & 0x1f);
+ m_latch = (data & 0xc0) >> 6;
+}
+
+WRITE8_MEMBER(nes_edu2k_device::write_m)
+{
+ LOG_MMC(("edu2k write_m, offset: %04x, data: %02x\n", offset, data));
+ m_prgram[((m_latch * 0x2000) + offset) & (m_prgram.size() - 1)] = data;
+}
+
+READ8_MEMBER(nes_edu2k_device::read_m)
+{
+ LOG_MMC(("edu2k read_m, offset: %04x\n", offset));
+ return m_prgram[((m_latch * 0x2000) + offset) & (m_prgram.size() - 1)];
+}
+
+/*-------------------------------------------------
+
+ Board UNL-T-230
+
+ Games: Dragon Ball Z IV (Unl)
+
+ This mapper appears to be similar to Konami VRC-2
+ but the game has no VROM and only 1 VRAM bank, so we
+ completely skip the chr bankswitch. If other games
+ using the same board and using CHR should surface,
+ we need to investigate this...
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+// Identical to Konami IRQ
+void nes_t230_device::hblank_irq(int scanline, int vblank, int blanked)
+{
+ /* Increment & check the IRQ scanline counter */
+ if (m_irq_enable && (++m_irq_count == 0x100))
+ {
+ m_irq_count = m_irq_count_latch;
+ m_irq_enable = m_irq_enable_latch;
+ m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE);
+ }
+}
+
+WRITE8_MEMBER(nes_t230_device::write_h)
+{
+ LOG_MMC(("t230 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x700c)
+ {
+ case 0x0000:
+ break;
+ case 0x2000:
+ prg16_89ab(data);
+ break;
+ case 0x1000:
+ case 0x1004:
+ case 0x1008:
+ case 0x100c:
+ switch (data & 0x03)
+ {
+ case 0x00: set_nt_mirroring(PPU_MIRROR_VERT); break;
+ case 0x01: set_nt_mirroring(PPU_MIRROR_HORZ); break;
+ case 0x02: set_nt_mirroring(PPU_MIRROR_LOW); break;
+ case 0x03: set_nt_mirroring(PPU_MIRROR_HIGH); break;
+ }
+ break;
+
+ case 0x7000:
+ m_irq_count_latch &= ~0x0f;
+ m_irq_count_latch |= data & 0x0f;
+ break;
+ case 0x7004:
+ m_irq_count_latch &= ~0xf0;
+ m_irq_count_latch |= (data << 4) & 0xf0;
+ break;
+ case 0x7008:
+ m_irq_mode = data & 0x04; // currently not implemented: 0 = prescaler mode / 1 = CPU mode
+ m_irq_enable = data & 0x02;
+ m_irq_enable_latch = data & 0x01;
+ if (data & 0x02)
+ m_irq_count = m_irq_count_latch;
+ break;
+
+ default:
+ logerror("unl_t230_w uncaught offset: %04x value: %02x\n", offset, data);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Bootleg Board for MK2
+
+ Games: Mortal Kombat II, Street Fighter III, Super Mario
+ Kart Rider
+
+ This board uses an IRQ system very similar to MMC3. We indeed
+ use mapper4_irq, but there is some small glitch!
+
+ iNES: mapper 91
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+// Same IRQ as MMC3
+void nes_mk2_device::hblank_irq( int scanline, int vblank, int blanked )
+{
+ if (scanline < PPU_BOTTOM_VISIBLE_SCANLINE)
+ {
+ int prior_count = m_irq_count;
+ if ((m_irq_count == 0) || m_irq_clear)
+ m_irq_count = m_irq_count_latch;
+ else
+ m_irq_count--;
+
+ if (m_irq_enable && !blanked && (m_irq_count == 0) && (prior_count || m_irq_clear))
+ {
+ LOG_MMC(("irq fired, scanline: %d (MAME %d, beam pos: %d)\n", scanline,
+ machine().first_screen()->vpos(), machine().first_screen()->hpos()));
+ m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE);
+ }
+ }
+ m_irq_clear = 0;
+}
+
+WRITE8_MEMBER(nes_mk2_device::write_m)
+{
+ LOG_MMC(("mk2 write_m, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x1000)
+ {
+ case 0x0000:
+ switch (offset & 0x03)
+ {
+ case 0x00: chr2_0(data, CHRROM); break;
+ case 0x01: chr2_2(data, CHRROM); break;
+ case 0x02: chr2_4(data, CHRROM); break;
+ case 0x03: chr2_6(data, CHRROM); break;
+ }
+ break;
+ case 0x1000:
+ switch (offset & 0x03)
+ {
+ case 0x00: prg8_89(data); break;
+ case 0x01: prg8_ab(data); break;
+ case 0x02: m_irq_enable = 0; m_irq_count = 0; break;
+ case 0x03: m_irq_enable = 1; m_irq_count = 7; break;
+ }
+ break;
+ default:
+ logerror("mk2 write_m, uncaught addr: %04x value: %02x\n", offset + 0x6000, data);
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+
+ UNL-WOLRDHERO board emulation
+
+
+ iNES:
+
+ -------------------------------------------------*/
+
+// Same as Konami VRC IRQ...
+void nes_whero_device::hblank_irq(int scanline, int vblank, int blanked)
+{
+ /* Increment & check the IRQ scanline counter */
+ if (m_irq_enable && (++m_irq_count == 0x100))
+ {
+ m_irq_count = m_irq_count_latch;
+ m_irq_enable = m_irq_enable_latch;
+ m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE);
+ }
+}
+
+WRITE8_MEMBER(nes_whero_device::write_h)
+{
+ int bank, shift, mask1, mask2;
+ LOG_MMC(("World Hero write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x70c3)
+ {
+ case 0x0000:
+ if (!m_reg)
+ prg8_89(data);
+ else
+ prg8_cd(data);
+ break;
+
+ case 0x1000:
+ switch (data & 0x03)
+ {
+ case 0x00: set_nt_mirroring(PPU_MIRROR_VERT); break;
+ case 0x01: set_nt_mirroring(PPU_MIRROR_HORZ); break;
+ case 0x02: set_nt_mirroring(PPU_MIRROR_LOW); break;
+ case 0x03: set_nt_mirroring(PPU_MIRROR_HIGH); break;
+ }
+ break;
+
+ case 0x1002:
+ case 0x1080:
+ if (m_reg != (data & 2))
+ {
+ m_reg = data & 2;
+ // swap banks!
+ prg8_89(m_prg_bank[2]);
+ prg8_cd(m_prg_bank[0]);
+ }
+ break;
+
+ case 0x2000:
+ prg8_ab(data);
+ break;
+
+ case 0x3000: case 0x3001: case 0x3002: case 0x3003:
+ case 0x4000: case 0x4001: case 0x4002: case 0x4003:
+ case 0x5000: case 0x5001: case 0x5002: case 0x5003:
+ case 0x6000: case 0x6001: case 0x6002: case 0x6003:
+ bank = ((offset & 0x7000) - 0x3000) / 0x0800 + BIT(offset, 1);
+ shift = (offset & 1) << 2;
+ mask1 = shift ? 0x00f : 0xff0;
+ mask2 = shift ? 0xff0 : 0x00f;
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & mask1) | ((data << shift) & mask2);
+ chr1_x(bank, m_mmc_vrom_bank[bank], CHRROM);
+ break;
+
+ case 0x7000:
+ m_irq_count_latch = (m_irq_count_latch & 0xf0) | (data & 0x0f);
+ break;
+
+ case 0x7001:
+ m_irq_count_latch = (m_irq_count_latch & 0x0f) | ((data & 0x0f) << 4);
+ break;
+
+ case 0x7002:
+ // m_irq_mode = data & 0x04; // currently not implemented: 0 = prescaler mode / 1 = CPU mode
+ m_irq_enable = data & 0x02;
+ m_irq_enable_latch = data & 0x01;
+ if (data & 0x02)
+ m_irq_count = m_irq_count_latch;
+ break;
+
+ case 0x7003:
+ m_irq_enable = m_irq_enable_latch;
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+
+ UNL-43272
+
+ Games: Gaau Hok Gwong Cheung
+
+ In MESS: Preliminary Supported
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_43272_device::write_h)
+{
+ LOG_MMC(("unl_43272 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ if ((m_latch & 0x81) == 0x81)
+ prg32((m_latch & 0x38) >> 3);
+
+ m_latch = offset & 0xffff;
+}
+
+
+READ8_MEMBER(nes_43272_device::read_h)
+{
+ UINT8 mask = (m_latch & 0x400) ? 0xfe : 0xff;
+ LOG_MMC(("unl_43272 read_h, offset: %04x\n", offset));
+
+ return hi_access_rom(offset & mask);
+}
+
+
+/*-------------------------------------------------
+
+ UNL-TF1201
+
+ Games:
+
+ In MESS: Preliminary Supported
+
+ -------------------------------------------------*/
+
+void nes_tf1201_device::hblank_irq(int scanline, int vblank, int blanked)
+{
+ if (m_irq_enable_latch != m_irq_enable && scanline < 240)
+ m_irq_count -= 8;
+
+ if (m_irq_enable)
+ {
+ m_irq_count++;
+ if ((m_irq_count & 0xff) == 238)
+ m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE);
+ }
+}
+
+void nes_tf1201_device::update_prg()
+{
+ prg8_89(m_swap ? 0xff : m_prg);
+ prg8_cd(m_swap ? m_prg : 0xff );
+}
+
+WRITE8_MEMBER(nes_tf1201_device::write_h)
+{
+ int bank;
+ LOG_MMC(("unl_tf1201 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ offset = (offset & 0x7003) | ((offset & 0x0c) >> 2); // nestopia does not OR here...
+
+ switch (offset & 0x7003)
+ {
+ case 0x0000:
+ m_prg = data;
+ update_prg();
+ break;
+ case 0x1000:
+ set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ break;
+ case 0x1001:
+ m_swap = data & 0x03;
+ update_prg();
+ break;
+ case 0x2000:
+ prg8_ab(data);
+ break;
+ case 0x3000: case 0x3001: case 0x3002: case 0x3003:
+ case 0x4000: case 0x4001: case 0x4002: case 0x4003:
+ case 0x5000: case 0x5001: case 0x5002: case 0x5003:
+ case 0x6000: case 0x6001: case 0x6002: case 0x6003:
+ bank = (((offset - 0x3000) >> 11) | (offset & 0x1)) & 0x7;
+ if (offset & 2)
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4);
+ else
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | ((data & 0x0f) << 0);
+ chr1_x(bank, m_mmc_vrom_bank[bank], m_chr_source);
+ break;
+ case 0x7000:
+ m_irq_count = (m_irq_count & 0xf0) | (data & 0x0f);
+ break;
+ case 0x7001:
+ case 0x7003:
+ m_irq_enable_latch = m_irq_enable;
+ m_irq_enable = BIT(data, 1);
+ break;
+ case 0x7002:
+ m_irq_count = (m_irq_count & 0x0f) | ((data & 0x0f) << 4);
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+
+ UNL-CITYFIGHT
+
+ Games:
+
+ Additional audio register not emulated yet!
+
+ In MESS: Preliminary Supported
+
+ -------------------------------------------------*/
+
+void nes_cityfight_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if (id == TIMER_IRQ)
+ {
+ if (m_irq_enable)
+ {
+ if (!m_irq_count)
+ {
+ m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE);
+ m_irq_count = 0xffff;
+ }
+ else
+ m_irq_count--;
+ }
+ }
+}
+
+void nes_cityfight_device::update_prg()
+{
+ prg32(m_prg_reg >> 2);
+ if (!m_prg_mode)
+ prg8_cd(m_prg_reg);
+}
+
+WRITE8_MEMBER(nes_cityfight_device::write_h)
+{
+ int bank;
+ LOG_MMC(("unl_cityfight write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x700c)
+ {
+ case 0x1000:
+ switch (data & 0x03)
+ {
+ case 0x00: set_nt_mirroring(PPU_MIRROR_VERT); break;
+ case 0x01: set_nt_mirroring(PPU_MIRROR_HORZ); break;
+ case 0x02: set_nt_mirroring(PPU_MIRROR_LOW); break;
+ case 0x03: set_nt_mirroring(PPU_MIRROR_HIGH); break;
+ }
+ m_prg_reg = data & 0xc;
+ break;
+ case 0x1004:
+ case 0x1008:
+ case 0x100c:
+ if (offset & 0x800)
+ LOG_MMC(("Extended Audio write, data %x!", data & 0x0f));//pcmwrite(0x4011, (V & 0xf) << 3);
+ else
+ m_prg_reg = data & 0xc;
+ break;
+
+
+ case 0x2000: case 0x2004: case 0x2008: case 0x200c:
+ bank = 2 + BIT(offset, 3);
+ if (offset & 4)
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4);
+ else
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | ((data & 0x0f) << 0);
+ chr1_x(bank, m_mmc_vrom_bank[bank], m_chr_source);
+ break;
+ case 0x3000: case 0x3004: case 0x3008: case 0x300c:
+ bank = 4 + BIT(offset, 3);
+ if (offset & 4)
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4);
+ else
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | ((data & 0x0f) << 0);
+ chr1_x(bank, m_mmc_vrom_bank[bank], m_chr_source);
+ break;
+ case 0x5000: case 0x5004: case 0x5008: case 0x500c:
+ bank = 0 + BIT(offset, 3);
+ if (offset & 4)
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4);
+ else
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | ((data & 0x0f) << 0);
+ chr1_x(bank, m_mmc_vrom_bank[bank], m_chr_source);
+ break;
+ case 0x6000: case 0x6004: case 0x6008: case 0x600c:
+ bank = 6 + BIT(offset, 3);
+ if (offset & 4)
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4);
+ else
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | ((data & 0x0f) << 0);
+ chr1_x(bank, m_mmc_vrom_bank[bank], m_chr_source);
+ break;
+
+ case 0x4000:
+ case 0x4004:
+ case 0x4008:
+ case 0x400c:
+ m_prg_mode = data & 1;
+
+ case 0x7000:
+ m_irq_count = (m_irq_count & 0x1e0) | ((data & 0x0f) << 1);
+ break;
+ case 0x7004:
+ m_irq_count = (m_irq_count & 0x1e) | ((data & 0x0f) << 5);
+ break;
+ case 0x7008:
+ m_irq_enable = BIT(data, 1);
+ break;
+ }
+}
+
+
+#ifdef UNUSED_FUNCTION
+/*-------------------------------------------------
+
+ FUJIYA Board - mapper 170 according to NEStopia
+
+ Is this possibly for the Fujiya Famikase series
+ of educational titles? Is there any dump around?
+
+ -------------------------------------------------*/
+
+void nes_fujiya_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+}
+
+void nes_fujiya_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(0);
+ chr8(0, m_chr_source);
+
+ m_latch = 0;
+}
+
+WRITE8_MEMBER(nes_fujiya_device::write_m)
+{
+ LOG_MMC(("fujiya write_m, offset: %04x, data: %02x\n", offset, data));
+ offset += 0x6000;
+
+ if (offset == 0x6502 || offset == 0x7000)
+ m_latch = (data & 0x40) << 1;
+}
+
+READ8_MEMBER(nes_fujiya_device::read_m)
+{
+ LOG_MMC(("fujiya read_m, offset: %04x\n", offset));
+ offset += 0x6000;
+
+ if (offset == 0x7001 || offset == 0x7777)
+ return m_latch | ((offset >> 8) & 0x7f);
+
+ return m_open_bus; // open bus
+}
+#endif
diff --git a/src/devices/bus/nes/pirate.h b/src/devices/bus/nes/pirate.h
new file mode 100644
index 00000000000..f11c53ced0d
--- /dev/null
+++ b/src/devices/bus/nes/pirate.h
@@ -0,0 +1,399 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_PIR_H
+#define __NES_PIR_H
+
+#include "nxrom.h"
+
+
+// ======================> nes_agci_device
+
+class nes_agci_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_agci_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_dreamtech_device
+
+class nes_dreamtech_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_dreamtech_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_fukutake_device
+
+class nes_fukutake_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_fukutake_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_l);
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+ UINT8 m_ram[0xb00];
+};
+
+
+// ======================> nes_futuremedia_device
+
+class nes_futuremedia_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_futuremedia_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void hblank_irq(int scanline, int vblank, int blanked);
+ virtual void pcb_reset();
+
+private:
+ UINT16 m_irq_count, m_irq_count_latch;
+ UINT8 m_irq_clear;
+ int m_irq_enable;
+};
+
+
+// ======================> nes_magseries_device
+
+class nes_magseries_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_magseries_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_daou306_device
+
+class nes_daou306_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_daou306_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_reg[16];
+};
+
+
+// ======================> nes_subor0_device
+
+class nes_subor0_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_subor0_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_reg[4];
+};
+
+
+// ======================> nes_subor1_device
+
+class nes_subor1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_subor1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_reg[4];
+};
+
+
+// ======================> nes_cc21_device
+
+class nes_cc21_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_cc21_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_xiaozy_device
+
+class nes_xiaozy_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_xiaozy_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_edu2k_device
+
+class nes_edu2k_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_edu2k_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+};
+
+
+// ======================> nes_t230_device
+
+class nes_t230_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_t230_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void hblank_irq(int scanline, int vblank, int blanked);
+ virtual void pcb_reset();
+
+private:
+ UINT16 m_irq_count, m_irq_count_latch;
+ UINT8 m_irq_mode;
+ int m_irq_enable, m_irq_enable_latch;
+
+ UINT8 m_mmc_vrom_bank[8];
+};
+
+
+// ======================> nes_mk2_device
+
+class nes_mk2_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_mk2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+
+ virtual void hblank_irq(int scanline, int vblank, int blanked);
+ virtual void pcb_reset();
+
+private:
+ UINT16 m_irq_count, m_irq_count_latch;
+ UINT8 m_irq_clear;
+ int m_irq_enable;
+};
+
+
+// ======================> nes_whero_device
+
+class nes_whero_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_whero_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void hblank_irq(int scanline, int vblank, int blanked);
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_reg;
+ UINT8 m_mmc_vrom_bank[8];
+
+ UINT16 m_irq_count, m_irq_count_latch;
+ int m_irq_enable, m_irq_enable_latch;
+// int m_irq_mode;
+};
+
+
+// ======================> nes_43272_device
+
+class nes_43272_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_43272_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_h);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT16 m_latch;
+};
+
+
+// ======================> nes_tf1201_device
+
+class nes_tf1201_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_tf1201_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void hblank_irq(int scanline, int vblank, int blanked);
+ virtual void pcb_reset();
+
+private:
+ void update_prg();
+ UINT8 m_prg, m_swap;
+ UINT16 m_irq_count;
+ int m_irq_enable, m_irq_enable_latch;
+
+ UINT8 m_mmc_vrom_bank[8];
+};
+
+
+// ======================> nes_cityfight_device
+
+class nes_cityfight_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_cityfight_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ void update_prg();
+ UINT8 m_prg_reg, m_prg_mode;
+ UINT16 m_irq_count;
+ int m_irq_enable;
+
+ static const device_timer_id TIMER_IRQ = 0;
+ emu_timer *irq_timer;
+
+ UINT8 m_mmc_vrom_bank[8];
+};
+
+
+
+#ifdef UNUSED_FUNCTION
+// ======================> nes_fujiya_device
+
+class nes_fujiya_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_fujiya_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(read_m);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+};
+#endif
+
+
+// device type definition
+extern const device_type NES_AGCI_50282;
+extern const device_type NES_DREAMTECH01;
+extern const device_type NES_FUKUTAKE;
+extern const device_type NES_FUTUREMEDIA;
+extern const device_type NES_MAGSERIES;
+extern const device_type NES_DAOU306;
+extern const device_type NES_SUBOR0;
+extern const device_type NES_SUBOR1;
+extern const device_type NES_CC21;
+extern const device_type NES_XIAOZY;
+extern const device_type NES_EDU2K;
+extern const device_type NES_T230;
+extern const device_type NES_MK2;
+extern const device_type NES_WHERO;
+extern const device_type NES_43272;
+extern const device_type NES_TF1201;
+extern const device_type NES_CITYFIGHT;
+extern const device_type NES_FUJIYA;
+
+#endif
diff --git a/src/devices/bus/nes/pt554.c b/src/devices/bus/nes/pt554.c
new file mode 100644
index 00000000000..4f6e843d8d1
--- /dev/null
+++ b/src/devices/bus/nes/pt554.c
@@ -0,0 +1,107 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for Bandai PT-554 PCBs
+
+
+ Here we emulate the following Bandai PT-554 PCB (a CNROM PCB + LPC / PARCOR speech synthesis chip)
+
+ TODO:
+ - emulate the mat controller
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "pt554.h"
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_BANDAI_PT554 = &device_creator<nes_bandai_pt554_device>;
+
+
+nes_bandai_pt554_device::nes_bandai_pt554_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_cnrom_device(mconfig, NES_BANDAI_PT554, "NES Cart Bandai PT-554 PCB", tag, owner, clock, "nes_bandai_pt554", __FILE__),
+ m_samples(*this, "samples")
+{
+}
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ Bandai PT-554 board emulation
+
+ This is used by Aerobics Studio. It is basically a CNROM board
+ with an Mitsubishi M50805 LPC / PARCOR speech synthesis chip
+ with internal tables stored in ROM which have not yet been dumped.
+
+ iNES: mapper 3?
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_bandai_pt554_device::write_m)
+{
+ LOG_MMC(("Bandai PT-554 Sound write, data: %02x\n", data));
+
+ if (!BIT(data, 6))
+ m_samples->start(data & 0x07, data & 0x07);
+ else
+ m_samples->stop(data & 0x07);
+}
+
+static const char *const pt554_sample_names[] =
+{
+ "*ftaerobi",
+ "00",
+ "01",
+ "02",
+ "03",
+ "04",
+ "05",
+ "06",
+ "07",
+ 0
+};
+
+//-------------------------------------------------
+// MACHINE_DRIVER
+//-------------------------------------------------
+
+static MACHINE_CONFIG_FRAGMENT( pt554 )
+
+ // additional sound hardware
+ MCFG_SPEAKER_STANDARD_MONO("addon")
+
+ MCFG_SOUND_ADD("samples", SAMPLES, 0)
+ MCFG_SAMPLES_CHANNELS(8)
+ MCFG_SAMPLES_NAMES(pt554_sample_names)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.50)
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions
+//-------------------------------------------------
+
+machine_config_constructor nes_bandai_pt554_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( pt554 );
+}
diff --git a/src/devices/bus/nes/pt554.h b/src/devices/bus/nes/pt554.h
new file mode 100644
index 00000000000..0d0bd7b60f4
--- /dev/null
+++ b/src/devices/bus/nes/pt554.h
@@ -0,0 +1,31 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_PT554_H
+#define __NES_PT554_H
+
+#include "nxrom.h"
+
+
+// ======================> nes_bandai_pt554_device
+
+class nes_bandai_pt554_device : public nes_cnrom_device
+{
+public:
+ // construction/destruction
+ nes_bandai_pt554_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual machine_config_constructor device_mconfig_additions() const;
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+
+private:
+ required_device<samples_device> m_samples;
+};
+
+
+
+
+// device type definition
+extern const device_type NES_BANDAI_PT554;
+
+#endif
diff --git a/src/devices/bus/nes/racermate.c b/src/devices/bus/nes/racermate.c
new file mode 100644
index 00000000000..6989c2254cb
--- /dev/null
+++ b/src/devices/bus/nes/racermate.c
@@ -0,0 +1,92 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for RacerMate PCBs
+
+
+ Here we emulate the UNL-RACERMATE PCB [mapper 168]
+
+ TODO:
+ - save VRAM
+ - emulate the bike controller?
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "racermate.h"
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_RACERMATE = &device_creator<nes_racermate_device>;
+
+
+nes_racermate_device::nes_racermate_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_RACERMATE, "NES Cart Racermate PCB", tag, owner, clock, "nes_racermate", __FILE__)
+{
+}
+
+
+
+void nes_racermate_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+}
+
+void nes_racermate_device::pcb_reset()
+{
+// m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr4_0(0, CHRRAM);
+ chr4_4(0, CHRRAM);
+
+ m_latch = 0;
+}
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ Board UNL-RACERMATE
+
+ In MESS: *VERY* preliminary support. Also, it seems that this
+ board saves to battery the CHRRAM!!!
+
+ -------------------------------------------------*/
+
+void nes_racermate_device::update_banks()
+{
+ chr4_4(m_latch & 0x0f, m_chr_source);
+ prg16_89ab(m_latch >> 6);
+}
+
+WRITE8_MEMBER(nes_racermate_device::write_h)
+{
+ LOG_MMC(("racermate write_h, offset: %04x, data: %02x\n", offset, data));
+
+ if (offset == 0x3000)
+ {
+ m_latch = data;
+ update_banks();
+ }
+}
diff --git a/src/devices/bus/nes/racermate.h b/src/devices/bus/nes/racermate.h
new file mode 100644
index 00000000000..209c2f56601
--- /dev/null
+++ b/src/devices/bus/nes/racermate.h
@@ -0,0 +1,35 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_RACERMATE_H
+#define __NES_RACERMATE_H
+
+#include "nxrom.h"
+
+
+// ======================> nes_racermate_device
+
+class nes_racermate_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_racermate_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ void update_banks();
+ UINT8 m_latch;
+};
+
+
+
+
+
+// device type definition
+extern const device_type NES_RACERMATE;
+
+#endif
diff --git a/src/devices/bus/nes/rcm.c b/src/devices/bus/nes/rcm.c
new file mode 100644
index 00000000000..28ec6e8277e
--- /dev/null
+++ b/src/devices/bus/nes/rcm.c
@@ -0,0 +1,303 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for RCM PCBs
+
+
+ Here we emulate the following PCBs
+
+ * RCM GS2015 [mapper 216]
+ * RCM GS2004
+ * RCM GS2013
+ * RCM Tetris Family 9in1 [mapper 61]
+ * RCM 3D Block
+
+ TODO:
+ - investigate why 3D Block does not work
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "rcm.h"
+
+#include "cpu/m6502/m6502.h"
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_GS2015 = &device_creator<nes_gs2015_device>;
+const device_type NES_GS2004 = &device_creator<nes_gs2004_device>;
+const device_type NES_GS2013 = &device_creator<nes_gs2013_device>;
+const device_type NES_TF9IN1 = &device_creator<nes_tf9_device>;
+const device_type NES_3DBLOCK = &device_creator<nes_3dblock_device>;
+
+
+nes_gs2015_device::nes_gs2015_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_GS2015, "NES Cart RCM GS-2015 PCB", tag, owner, clock, "nes_gs2015", __FILE__)
+{
+}
+
+nes_gs2004_device::nes_gs2004_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_GS2004, "NES Cart RCM GS-2004 PCB", tag, owner, clock, "nes_gs2004", __FILE__)
+{
+}
+
+nes_gs2013_device::nes_gs2013_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_GS2013, "NES Cart RCM GS-2013 PCB", tag, owner, clock, "nes_gs2013", __FILE__)
+{
+}
+
+nes_tf9_device::nes_tf9_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_TF9IN1, "NES Cart RCM Tetris Family 9 in 1 PCB", tag, owner, clock, "nes_tetrisfam", __FILE__)
+{
+}
+
+nes_3dblock_device::nes_3dblock_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_3DBLOCK, "NES Cart RCM 3D Block PCB", tag, owner, clock, "nes_3dblock", __FILE__)
+{
+}
+
+
+
+
+void nes_gs2015_device::device_start()
+{
+ common_start();
+}
+
+void nes_gs2015_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_gs2004_device::device_start()
+{
+ common_start();
+}
+
+void nes_gs2004_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0xff);
+ chr8(0, m_chr_source);
+}
+
+void nes_gs2013_device::device_start()
+{
+ common_start();
+}
+
+void nes_gs2013_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0xff);
+ chr8(0, m_chr_source);
+}
+
+void nes_tf9_device::device_start()
+{
+ common_start();
+}
+
+void nes_tf9_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_3dblock_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_reg));
+ save_item(NAME(m_irq_count));
+}
+
+void nes_3dblock_device::pcb_reset()
+{
+ prg32(0);
+ chr8(0, CHRRAM);
+ m_reg[0] = 0;
+ m_reg[1] = 0;
+ m_reg[2] = 0;
+ m_reg[3] = 0;
+ m_irq_count = 0;
+}
+
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ RCM GS2015 Board
+
+ Games: Bonza, Magic Jewelry 2
+
+ Very simple mapper: writes to 0x8000-0xffff sets prg32
+ to offset and chr8 to offset>>1 (when chrrom is present)
+
+ iNES: mapper 216
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_gs2015_device::write_h)
+{
+ LOG_MMC(("gs2015 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ prg32(offset);
+ chr8(offset >> 1, m_chr_source);
+}
+
+READ8_MEMBER(nes_gs2015_device::read_m)
+{
+ LOG_MMC(("gs2015 read_m, offset: %04x\n", offset));
+ return 0; // Videopoker Bonza needs this (sort of protection? or related to inputs?)
+}
+
+/*-------------------------------------------------
+
+ Board BMC-GS2004
+
+ Games: Tetris Family 6-in-1
+
+ In MESS: Preliminary Support. It also misses WRAM handling
+ (we need reads from 0x6000-0x7fff)
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_gs2004_device::write_h)
+{
+ LOG_MMC(("gs2004 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ prg32(data);
+}
+
+/*-------------------------------------------------
+
+ Board BMC-GS2013
+
+ Games: Tetris Family 12-in-1
+
+ In MESS: Preliminary Support. It also misses WRAM handling
+ (we need reads from 0x6000-0x7fff)
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_gs2013_device::write_h)
+{
+ LOG_MMC(("gs2013 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ if (data & 0x08)
+ prg32(data & 0x09);
+ else
+ prg32(data & 0x07);
+}
+
+/*-------------------------------------------------
+
+ Bootleg Board by RCM for Tetris Family
+
+ Games: Tetris Family 9 in 1, 20 in 1
+
+ Simple Mapper: prg/chr/nt are swapped depending on the offset
+ of writes in 0x8000-0xffff. offset&0x80 set NT mirroring,
+ when (offset&0x30) is 0,3 prg32 is set; when it is 1,2
+ two 16k prg banks are set. See below for the values used in
+ these banks.
+
+ iNES: mapper 61
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_tf9_device::write_h)
+{
+ LOG_MMC(("tetrisfam write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x30)
+ {
+ case 0x00:
+ case 0x30:
+ prg32(offset & 0x0f);
+ break;
+ case 0x10:
+ case 0x20:
+ prg16_89ab(((offset & 0x0f) << 1) | ((offset & 0x20) >> 4));
+ prg16_cdef(((offset & 0x0f) << 1) | ((offset & 0x20) >> 4));
+ break;
+ }
+ set_nt_mirroring(BIT(data, 7) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+}
+
+/*-------------------------------------------------
+
+ Bootleg Board by RCM for 3-D Block
+
+ Games: 3-D Block Hwang Shinwei version
+
+ iNES:
+
+ In MESS: Very Preliminary Support. What is the purpose
+ of the writes to $4800-$4900-$4a00? These writes
+ also happens on the RCM version, which however works
+ (probably an unused leftover code in that version)
+
+ FCEUmm suggests it might be IRQ related, but
+ it does not seem to help much...
+
+ -------------------------------------------------*/
+
+void nes_3dblock_device::hblank_irq(int scanline, int vblank, int blanked)
+{
+ if (m_irq_count)
+ {
+ m_irq_count--;
+ if (!m_irq_count)
+ m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE);
+ }
+}
+
+WRITE8_MEMBER(nes_3dblock_device::write_l)
+{
+ LOG_MMC(("3dblock write_l, offset: %04x, data: %02x\n", offset, data));
+ offset += 0x100;
+
+ switch (offset)
+ {
+ case 0x800: // $4800
+ m_reg[0] = data;
+ break;
+ case 0x900: // $4900
+ m_reg[1] = data;
+ break;
+ case 0xa00: // $4a00
+ m_reg[2] = data;
+ break;
+ case 0xe00: // $4e00
+ m_reg[3] = data; m_irq_count = 0x10;
+ break;
+ }
+}
diff --git a/src/devices/bus/nes/rcm.h b/src/devices/bus/nes/rcm.h
new file mode 100644
index 00000000000..4a5c05b6aa9
--- /dev/null
+++ b/src/devices/bus/nes/rcm.h
@@ -0,0 +1,105 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_RCM_H
+#define __NES_RCM_H
+
+#include "nxrom.h"
+
+
+// ======================> nes_gs2015_device
+
+class nes_gs2015_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_gs2015_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_l) { return read_m(space, offset, mem_mask); }
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_gs2004_device
+
+class nes_gs2004_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_gs2004_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_gs2013_device
+
+class nes_gs2013_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_gs2013_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_tf9_device
+
+class nes_tf9_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_tf9_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_3dblock_device
+
+class nes_3dblock_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_3dblock_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+
+ virtual void hblank_irq(int scanline, int vblank, int blanked);
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_reg[4];
+ UINT8 m_irq_count;
+};
+
+
+
+
+// device type definition
+extern const device_type NES_GS2015;
+extern const device_type NES_GS2004;
+extern const device_type NES_GS2013;
+extern const device_type NES_TF9IN1;
+extern const device_type NES_3DBLOCK;
+
+#endif
diff --git a/src/devices/bus/nes/rexsoft.c b/src/devices/bus/nes/rexsoft.c
new file mode 100644
index 00000000000..c0e261dc5fa
--- /dev/null
+++ b/src/devices/bus/nes/rexsoft.c
@@ -0,0 +1,262 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for Rex Soft PCBs
+
+
+ Here we emulate the following PCBs
+
+ * Rex Soft DragonBall Z V [mapper 12]
+ * Rex Soft SL-1632 [mapper 14]
+
+ TODO:
+ - fix 0x6000-0x7fff accesses, write_m/read_m
+ - check glitches in SL-1632
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "rexsoft.h"
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_REX_DBZ5 = &device_creator<nes_rex_dbz5_device>;
+const device_type NES_REX_SL1632 = &device_creator<nes_rex_sl1632_device>;
+
+
+nes_rex_dbz5_device::nes_rex_dbz5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_txrom_device(mconfig, NES_REX_DBZ5, "NES Cart Rex Soft DragonBall Z V PCB", tag, owner, clock, "nes_rex_dbz5", __FILE__)
+{
+}
+
+nes_rex_sl1632_device::nes_rex_sl1632_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_txrom_device(mconfig, NES_REX_SL1632, "NES Cart Rex Soft SL-1632 PCB", tag, owner, clock, "nes_rex_sl1632", __FILE__)
+{
+}
+
+
+
+
+void nes_rex_dbz5_device::device_start()
+{
+ mmc3_start();
+ save_item(NAME(m_extra));
+}
+
+void nes_rex_dbz5_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ m_extra = 0;
+ mmc3_common_initialize(0xff, 0xff, 0);
+}
+
+void nes_rex_sl1632_device::device_start()
+{
+ mmc3_start();
+ save_item(NAME(m_mode));
+ save_item(NAME(m_mirror));
+ save_item(NAME(m_extra_bank));
+}
+
+void nes_rex_sl1632_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+
+ m_mode = 0;
+ m_mirror = 0;
+ memset(m_extra_bank, 0, sizeof(m_extra_bank));
+ m_extra_bank[2] = 0xfe;
+ m_extra_bank[3] = 0xff;
+ mmc3_common_initialize(0xff, 0xff, 0);
+}
+
+
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ Bootleg Board by Rex Soft
+
+ Games: Dragon Ball Z 5, Dragon Ball Z Super
+
+ MMC3 clone. Writes to 0x4100-0x5fff (or from 0x4020?)
+ possibly select higher VROM pages (to allow up to
+ 512 banks instead of 256 only): bit0 selects the
+ higher pages for PPU banks 0-3 (0x0000-0x0fff), while
+ bit4 selects the higher pages for PPU banks 4-7 (0x1000-0x1fff)
+
+ iNES: mapper 12
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_rex_dbz5_device::write_l)
+{
+ LOG_MMC(("rex_dbz write_l, offset: %04x, data: %02x\n", offset, data));
+
+ m_extra = data;
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+}
+
+/* we would need to use this read handler in 0x6000-0x7fff as well */
+READ8_MEMBER(nes_rex_dbz5_device::read_l)
+{
+ LOG_MMC(("rex_dbz read_l, offset: %04x\n", offset));
+ return 0x01;
+}
+
+void nes_rex_dbz5_device::chr_cb(int start, int bank, int source)
+{
+ int shift = (start < 4) ? 8 : 4;
+
+ bank |= ((m_extra << shift) & 0x100);
+ chr1_x(start, bank, source);
+}
+
+/*-------------------------------------------------
+
+ Rex Soft SL1632 Board
+
+ Games: Samurai Spirits
+
+ MMC3 clone
+
+ iNES: mapper 14
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+void nes_rex_sl1632_device::set_prg(int prg_base, int prg_mask)
+{
+ if (m_mode & 0x02)
+ {
+ // here standard MMC3 bankswitch
+ UINT8 prg_flip = (m_latch & 0x40) ? 2 : 0;
+
+ prg_cb(0, prg_base | (m_mmc_prg_bank[0 ^ prg_flip] & prg_mask));
+ prg_cb(1, prg_base | (m_mmc_prg_bank[1] & prg_mask));
+ prg_cb(2, prg_base | (m_mmc_prg_bank[2 ^ prg_flip] & prg_mask));
+ prg_cb(3, prg_base | (m_mmc_prg_bank[3] & prg_mask));
+ }
+ else
+ {
+ prg8_89(m_extra_bank[0]);
+ prg8_ab(m_extra_bank[1]);
+ prg8_cd(m_extra_bank[2]);
+ prg8_ef(m_extra_bank[3]);
+ }
+}
+
+void nes_rex_sl1632_device::set_chr(UINT8 chr, int chr_base, int chr_mask)
+{
+ static const UINT8 conv_table[8] = {5, 5, 5, 5, 3, 3, 1, 1};
+ UINT8 chr_page = (m_latch & 0x80) >> 5;
+ UINT8 bank[8];
+ UINT8 chr_base2[8];
+
+ if (m_mode & 0x02)
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ // since the mapper acts on 1K CHR chunks, we have 8 banks which use the 6 m_mmc_vrom_bank from MMC3 base class
+ if (i < 4)
+ bank[i] = ((m_mmc_vrom_bank[i / 2] & 0xfe) | (i & 1));
+ else
+ bank[i] = m_mmc_vrom_bank[i - 2];
+ chr_base2[i] = chr_base | ((m_mode << conv_table[i]) & 0x100);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < 8; i++)
+ {
+ bank[i] = m_extra_bank[i + 4]; // first 4 m_extra_banks are PRG
+ chr_base2[i] = chr_base;
+ }
+ }
+
+ chr1_x(chr_page ^ 0, chr_base2[0] | (bank[0] & chr_mask), chr);
+ chr1_x(chr_page ^ 1, chr_base2[1] | (bank[1] & chr_mask), chr);
+ chr1_x(chr_page ^ 2, chr_base2[2] | (bank[2] & chr_mask), chr);
+ chr1_x(chr_page ^ 3, chr_base2[3] | (bank[3] & chr_mask), chr);
+ chr1_x(chr_page ^ 4, chr_base2[4] | (bank[4] & chr_mask), chr);
+ chr1_x(chr_page ^ 5, chr_base2[5] | (bank[5] & chr_mask), chr);
+ chr1_x(chr_page ^ 6, chr_base2[6] | (bank[6] & chr_mask), chr);
+ chr1_x(chr_page ^ 7, chr_base2[7] | (bank[7] & chr_mask), chr);
+}
+
+WRITE8_MEMBER(nes_rex_sl1632_device::write_h)
+{
+ UINT8 helper1, helper2;
+ LOG_MMC(("rex_sl1632 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ if (offset == 0x2131)
+ {
+ m_mode = data;
+ set_prg(m_prg_base, m_prg_mask);
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+
+ if (!(m_mode & 0x02))
+ set_nt_mirroring(BIT(m_mirror, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ }
+
+ if (m_mode & 0x02)
+ {
+ switch (offset & 0x6001)
+ {
+ case 0x2000:
+ set_nt_mirroring(BIT(m_mirror, 0) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ);
+ break;
+
+ default:
+ txrom_write(space, offset, data, mem_mask);
+ break;
+ }
+ }
+ else if (offset >= 0x3000 && offset <= 0x6003)
+ {
+ helper1 = (offset & 0x01) << 2;
+ offset = ((offset & 0x02) | (offset >> 10)) >> 1;
+ helper2 = ((offset + 2) & 0x07) + 4; // '+4' because first 4 m_extra_banks are for PRG!
+ m_extra_bank[helper2] = (m_extra_bank[helper2] & (0xf0 >> helper1)) | ((data & 0x0f) << helper1);
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+ }
+ else
+ {
+ switch (offset & 0x7003)
+ {
+ case 0x0000:
+ case 0x2000:
+ m_extra_bank[offset >> 13] = data;
+ set_prg(m_prg_base, m_prg_mask);
+ break;
+
+ case 0x1000:
+ m_mirror = data;
+ set_nt_mirroring(BIT(m_mirror, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ break;
+ }
+ }
+}
diff --git a/src/devices/bus/nes/rexsoft.h b/src/devices/bus/nes/rexsoft.h
new file mode 100644
index 00000000000..81527759574
--- /dev/null
+++ b/src/devices/bus/nes/rexsoft.h
@@ -0,0 +1,61 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_REXSOFT_H
+#define __NES_REXSOFT_H
+
+#include "mmc3.h"
+
+
+// ======================> nes_rex_dbz5_device
+
+class nes_rex_dbz5_device : public nes_txrom_device
+{
+public:
+ // construction/destruction
+ nes_rex_dbz5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_l);
+ virtual DECLARE_READ8_MEMBER(read_m) { return read_l(space, offset, mem_mask); }
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+ virtual void chr_cb( int start, int bank, int source );
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_extra;
+};
+
+
+// ======================> nes_rex_sl1632_device
+
+class nes_rex_sl1632_device : public nes_txrom_device
+{
+public:
+ // construction/destruction
+ nes_rex_sl1632_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+protected:
+ virtual void set_prg(int prg_base, int prg_mask);
+ virtual void set_chr(UINT8 chr, int chr_base, int chr_mask);
+
+ UINT8 m_mode, m_mirror;
+ UINT8 m_extra_bank[12];
+};
+
+
+
+
+
+// device type definition
+extern const device_type NES_REX_DBZ5;
+extern const device_type NES_REX_SL1632;
+
+#endif
diff --git a/src/devices/bus/nes/sachen.c b/src/devices/bus/nes/sachen.c
new file mode 100644
index 00000000000..87edbc6a700
--- /dev/null
+++ b/src/devices/bus/nes/sachen.c
@@ -0,0 +1,695 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for Sachen PCBs
+
+
+ Here we emulate the following PCBs
+
+ * Sachen SA-009 [mapper 160]
+ * Sachen SA-0036 [mapper 148]
+ * Sachen SA-0037 [mapper 149]
+ * Sachen SA-72007 [mapper 145]
+ * Sachen SA-72008 [mapper 133]
+ * Sachen TCA-01 [mapper 143]
+ * Sachen TCU-01 [mapper 147]
+ * Sachen TCU-02 [mapper 136]
+ * Sachen Discrete PCBs [mapper 150 & 243]
+ * Sachen 8259 [mapper 141 (A), 138 (B), 139 (C), 137 (D)]
+
+ Known issues on specific mappers:
+
+ * 133 Qi Wang starts with corrupted graphics (ingame seems better)
+
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "sachen.h"
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_SACHEN_SA009 = &device_creator<nes_sachen_sa009_device>;
+const device_type NES_SACHEN_SA0036 = &device_creator<nes_sachen_sa0036_device>;
+const device_type NES_SACHEN_SA0037 = &device_creator<nes_sachen_sa0037_device>;
+const device_type NES_SACHEN_SA72007 = &device_creator<nes_sachen_sa72007_device>;
+const device_type NES_SACHEN_SA72008 = &device_creator<nes_sachen_sa72008_device>;
+const device_type NES_SACHEN_TCA01 = &device_creator<nes_sachen_tca01_device>;
+const device_type NES_SACHEN_TCU01 = &device_creator<nes_sachen_tcu01_device>;
+const device_type NES_SACHEN_TCU02 = &device_creator<nes_sachen_tcu02_device>;
+const device_type NES_SACHEN_74X374 = &device_creator<nes_sachen_74x374_device>;
+const device_type NES_SACHEN_74X374_ALT = &device_creator<nes_sachen_74x374_alt_device>;
+const device_type NES_SACHEN_8259A = &device_creator<nes_sachen_8259a_device>;
+const device_type NES_SACHEN_8259B = &device_creator<nes_sachen_8259b_device>;
+const device_type NES_SACHEN_8259C = &device_creator<nes_sachen_8259c_device>;
+const device_type NES_SACHEN_8259D = &device_creator<nes_sachen_8259d_device>;
+
+
+nes_sachen_sa009_device::nes_sachen_sa009_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SACHEN_SA009, "NES Cart Sachen SA009 PCB", tag, owner, clock, "nes_sa009", __FILE__)
+{
+}
+
+nes_sachen_sa0036_device::nes_sachen_sa0036_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SACHEN_SA0036, "NES Cart Sachen sa0036 PCB", tag, owner, clock, "nes_sa0036", __FILE__)
+{
+}
+
+nes_sachen_sa0037_device::nes_sachen_sa0037_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SACHEN_SA0037, "NES Cart Sachen sa0037 PCB", tag, owner, clock, "nes_sa0037", __FILE__)
+{
+}
+
+nes_sachen_sa72007_device::nes_sachen_sa72007_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SACHEN_SA72007, "NES Cart Sachen SA72007 PCB", tag, owner, clock, "nes_sa72007", __FILE__)
+{
+}
+
+nes_sachen_sa72008_device::nes_sachen_sa72008_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SACHEN_SA72008, "NES Cart Sachen SA72008 PCB", tag, owner, clock, "nes_sa72008", __FILE__)
+{
+}
+
+nes_sachen_tca01_device::nes_sachen_tca01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SACHEN_TCA01, "NES Cart Sachen TCA-01 PCB", tag, owner, clock, "nes_tca01", __FILE__)
+{
+}
+
+nes_sachen_tcu01_device::nes_sachen_tcu01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SACHEN_TCU01, "NES Cart Sachen TCU-01 PCB", tag, owner, clock, "nes_tcu01", __FILE__)
+{
+}
+
+nes_sachen_tcu02_device::nes_sachen_tcu02_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SACHEN_TCU02, "NES Cart Sachen TCU-02 PCB", tag, owner, clock, "nes_tcu02", __FILE__)
+{
+}
+
+nes_sachen_74x374_device::nes_sachen_74x374_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : nes_nrom_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+nes_sachen_74x374_device::nes_sachen_74x374_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SACHEN_74X374, "NES Cart Sachen 74*374 PCB", tag, owner, clock, "nes_s74x34", __FILE__)
+{
+}
+
+nes_sachen_74x374_alt_device::nes_sachen_74x374_alt_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_sachen_74x374_device(mconfig, NES_SACHEN_74X374_ALT, "NES Cart Sachen 74*374 Alt PCB", tag, owner, clock, "nes_s74x34a", __FILE__)
+{
+}
+
+nes_sachen_8259a_device::nes_sachen_8259a_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : nes_sachen_74x374_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+nes_sachen_8259a_device::nes_sachen_8259a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_sachen_74x374_device(mconfig, NES_SACHEN_8259A, "NES Cart Sachen 8259A PCB", tag, owner, clock, "nes_s8259a", __FILE__)
+{
+}
+
+nes_sachen_8259b_device::nes_sachen_8259b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_sachen_8259a_device(mconfig, NES_SACHEN_8259B, "NES Cart Sachen 8259B PCB", tag, owner, clock, "nes_s8259b", __FILE__)
+{
+}
+
+nes_sachen_8259c_device::nes_sachen_8259c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_sachen_8259a_device(mconfig, NES_SACHEN_8259C, "NES Cart Sachen 8259C PCB", tag, owner, clock, "nes_s8259c", __FILE__)
+{
+}
+
+nes_sachen_8259d_device::nes_sachen_8259d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_sachen_8259a_device(mconfig, NES_SACHEN_8259D, "NES Cart Sachen 8259D PCB", tag, owner, clock, "nes_s8259d", __FILE__)
+{
+}
+
+
+
+
+void nes_sachen_sa009_device::device_start()
+{
+ common_start();
+}
+
+void nes_sachen_sa009_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_sachen_sa0036_device::device_start()
+{
+ common_start();
+}
+
+void nes_sachen_sa0036_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_sachen_sa0037_device::device_start()
+{
+ common_start();
+}
+
+void nes_sachen_sa0037_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_sachen_sa72007_device::device_start()
+{
+ common_start();
+}
+
+void nes_sachen_sa72007_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_sachen_sa72008_device::device_start()
+{
+ common_start();
+}
+
+void nes_sachen_sa72008_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_sachen_tca01_device::device_start()
+{
+ common_start();
+}
+
+void nes_sachen_tca01_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(1);
+ chr8(0, m_chr_source);
+}
+
+void nes_sachen_tcu01_device::device_start()
+{
+ common_start();
+}
+
+void nes_sachen_tcu01_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_sachen_tcu02_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+}
+
+void nes_sachen_tcu02_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+
+ m_latch = 0;
+}
+
+void nes_sachen_74x374_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+ save_item(NAME(m_mmc_vrom_bank));
+}
+
+void nes_sachen_74x374_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+
+ m_latch = 0;
+ m_mmc_vrom_bank = 0;
+}
+
+void nes_sachen_8259a_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+ save_item(NAME(m_reg));
+}
+
+void nes_sachen_8259a_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+ set_nt_mirroring(PPU_MIRROR_VERT);
+
+ m_latch = 0;
+ memset(m_reg, 0, sizeof(m_reg));
+}
+
+void nes_sachen_8259d_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(m_vrom_chunks - 1, CHRROM);
+ set_nt_mirroring(PPU_MIRROR_VERT);
+
+ m_latch = 0;
+ memset(m_reg, 0, sizeof(m_reg));
+}
+
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ Sachen SA009 bootleg boards
+
+ Games: Pipe 5
+
+ iNES: mapper 160
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_sachen_sa009_device::write_l)
+{
+ LOG_MMC(("SA009 write_l, offset: %04x, data: %02x\n", offset, data));
+
+ chr8(data, m_chr_source);
+}
+
+/*-------------------------------------------------
+
+ Sachen SA0036 bootleg boards
+
+ Games: Taiwan Mahjong 16
+
+ iNES: mapper 149
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_sachen_sa0036_device::write_h)
+{
+ LOG_MMC(("sa0036 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ chr8(data >> 7, CHRROM);
+}
+
+/*-------------------------------------------------
+
+ Sachen SA0037 bootleg boards
+
+ Games: Mahjong World, Shisen Mahjong
+
+ iNES: mapper 148
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_sachen_sa0037_device::write_h)
+{
+ LOG_MMC(("sa0037 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ // this pcb is subject to bus conflict
+ data = account_bus_conflict(offset, data);
+
+ prg32(data >> 3);
+ chr8(data, CHRROM);
+}
+
+/*-------------------------------------------------
+
+ Sachen SA72007 bootleg boards
+
+ Games: Sidewinder
+
+ iNES: mapper 145
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_sachen_sa72007_device::write_l)
+{
+ LOG_MMC(("SA72007 write_l, offset: %04x, data: %02x\n", offset, data));
+
+ /* only if we are at 0x4100 + k * 0x200, but 0x4100 is offset = 0 */
+ if (!(offset & 0x100))
+ chr8(data >> 7, CHRROM);
+}
+
+/*-------------------------------------------------
+
+ Sachen SA72008 bootleg boards
+
+ Games: Jovial Race, Qi Wang
+
+ iNES: mapper 133
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_sachen_sa72008_device::write_l)
+{
+ LOG_MMC(("SA72008 write_l, offset: %04x, data: %02x\n", offset, data));
+
+ prg32(data >> 2);
+ chr8(data, CHRROM);
+}
+
+/*-------------------------------------------------
+
+ Sachen TCA-01 bootleg boards
+
+ iNES: mapper 143
+
+ Games: Dancing Blocks, Magic Mathematic
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+READ8_MEMBER(nes_sachen_tca01_device::read_l)
+{
+ LOG_MMC(("TCA-01 read_l, offset: %04x\n", offset));
+
+ /* the address is read only if we are at 0x4100 + k * 0x200, but 0x4100 is offset = 0 */
+ if (!(offset & 0x100))
+ return (~offset & 0x3f) | 0x40;
+ else
+ return 0x00;
+}
+
+/*-------------------------------------------------
+
+ Sachen TCU-01 bootleg boards
+
+ Games: Challenge of the Dragon, Chinese Kungfu
+
+ iNES: mapper 147
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_sachen_tcu01_device::write_l)
+{
+ LOG_MMC(("TCU-01 write_l, offset: %04x, data: %02x\n", offset, data));
+
+ if ((offset & 0x103) == 0x002)
+ {
+ prg32(((data >> 6) & 0x02) | ((data >> 2) & 0x01));
+ chr8(data >> 3, CHRROM);
+ }
+}
+
+/*-------------------------------------------------
+
+ Sachen TCU-02 bootleg boards
+
+ Games: Wei Lai Xiao Zi
+
+ iNES: mapper 136
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_sachen_tcu02_device::write_l)
+{
+ LOG_MMC(("TCU-02 write_l, offset: %04x, data: %02x\n", offset, data));
+
+ if ((offset & 0x103) == 0x002)
+ {
+ m_latch = (data & 0x30) | ((data + 3) & 0x0f);
+ chr8(m_latch, CHRROM);
+ }
+}
+
+READ8_MEMBER(nes_sachen_tcu02_device::read_l)
+{
+ LOG_MMC(("TCU-02 read_l, offset: %04x\n", offset));
+
+ if ((offset & 0x103) == 0x000)
+ return m_latch | 0x40;
+ else
+ return 0x00;
+}
+
+/*-------------------------------------------------
+
+ Sachen 74x374 bootleg boards
+
+ Games: Chess Academy, Chinese Checkers Jpn, Mahjong Academy,
+ Olympic IQ, Poker II, Tasac [150], Poker III [243]
+
+ iNES: mappers 150 & 243
+
+ -------------------------------------------------*/
+
+void nes_sachen_74x374_device::set_mirror(UINT8 nt) // also used by mappers 137, 138, 139, 141
+{
+ switch (nt)
+ {
+ case 0:
+ case 1:
+ set_nt_mirroring(nt ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ break;
+ case 2:
+ set_nt_page(0, CIRAM, 0, 1);
+ set_nt_page(1, CIRAM, 1, 1);
+ set_nt_page(2, CIRAM, 1, 1);
+ set_nt_page(3, CIRAM, 1, 1);
+ break;
+ case 3:
+ set_nt_mirroring(PPU_MIRROR_LOW);
+ break;
+ default:
+ LOG_MMC(("Mapper set NT to invalid value %02x", nt));
+ break;
+ }
+}
+
+
+WRITE8_MEMBER(nes_sachen_74x374_device::write_l)
+{
+ LOG_MMC(("Sachen 74*374 write_l, offset: %04x, data: %02x\n", offset, data));
+
+ /* write happens only if we are at 0x4100 + k * 0x200, but 0x4100 is offset = 0 */
+ if (!(offset & 0x100))
+ {
+ if (!(offset & 0x01))
+ m_latch = data & 0x07;
+ else
+ {
+ switch (m_latch)
+ {
+ case 0x02:
+ m_mmc_vrom_bank = (m_mmc_vrom_bank & ~0x08) | ((data << 3) & 0x08);
+ chr8(m_mmc_vrom_bank, CHRROM);
+ prg32(data & 0x01);
+ break;
+ case 0x04:
+ m_mmc_vrom_bank = (m_mmc_vrom_bank & ~0x04) | ((data << 2) & 0x04);
+ chr8(m_mmc_vrom_bank, CHRROM);
+ break;
+ case 0x05:
+ prg32(data & 0x07);
+ break;
+ case 0x06:
+ m_mmc_vrom_bank = (m_mmc_vrom_bank & ~0x03) | ((data << 0) & 0x03);
+ chr8(m_mmc_vrom_bank, CHRROM);
+ break;
+ case 0x07:
+ set_mirror((data >> 1) & 0x03);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+READ8_MEMBER(nes_sachen_74x374_device::read_l)
+{
+ LOG_MMC(("Sachen 74*374 read_l, offset: %04x", offset));
+
+ /* read happens only if we are at 0x4100 + k * 0x200, but 0x4100 is offset = 0 */
+ if (!(offset & 0x100))
+ return (~m_latch & 0x3f) /* ^ dips*/; // we would need to check the Dips here
+ else
+ return 0;
+}
+
+WRITE8_MEMBER(nes_sachen_74x374_alt_device::write_l)
+{
+ LOG_MMC(("Sachen 74*374 Alt write_l, offset: %04x, data: %02x\n", offset, data));
+
+ /* write happens only if we are at 0x4100 + k * 0x200, but 0x4100 is offset = 0 */
+ if (!(offset & 0x100))
+ {
+ if (!(offset & 0x01))
+ m_latch = data;
+ else
+ {
+ switch (m_latch & 0x07)
+ {
+ case 0x00:
+ prg32(0);
+ chr8(3, CHRROM);
+ break;
+ case 0x02:
+ m_mmc_vrom_bank = (m_mmc_vrom_bank & ~0x08) | ((data << 3) & 0x08);
+ chr8(m_mmc_vrom_bank, CHRROM);
+ break;
+ case 0x04:
+ m_mmc_vrom_bank = (m_mmc_vrom_bank & ~0x01) | ((data << 0) & 0x01);
+ chr8(m_mmc_vrom_bank, CHRROM);
+ break;
+ case 0x05:
+ prg32(data & 0x01);
+ break;
+ case 0x06:
+ m_mmc_vrom_bank = (m_mmc_vrom_bank & ~0x06) | ((data << 1) & 0x06);
+ chr8(m_mmc_vrom_bank, CHRROM);
+ break;
+ case 0x07:
+ set_mirror(BIT(data, 0));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+/*-------------------------------------------------
+
+ Sachen S8259 bootleg boards
+
+ iNES: mapper 141 (A), 138 (B), 139 (C), 137 (D)
+
+ -------------------------------------------------*/
+
+void nes_sachen_8259a_device::chr_update()
+{
+ UINT8 bank_helper1 = m_reg[7] & 0x01;
+ UINT8 bank_helper2 = (m_reg[4] & 0x07) << 3;
+
+ if (m_chr_source == CHRROM)
+ {
+ chr2_0(((m_reg[bank_helper1 ? 0 : 0] & 0x07) | bank_helper2) << 1 | 0, CHRROM);
+ chr2_2(((m_reg[bank_helper1 ? 0 : 1] & 0x07) | bank_helper2) << 1 | 1, CHRROM);
+ chr2_4(((m_reg[bank_helper1 ? 0 : 2] & 0x07) | bank_helper2) << 1 | 0, CHRROM);
+ chr2_6(((m_reg[bank_helper1 ? 0 : 3] & 0x07) | bank_helper2) << 1 | 1, CHRROM);
+ }
+}
+
+WRITE8_MEMBER(nes_sachen_8259a_device::write_l)
+{
+ LOG_MMC(("Sachen 8259 write_l, offset: %04x, data: %02x\n", offset, data));
+
+ /* write happens only if we are at 0x4100 + k * 0x200, but 0x4100 is offset = 0 */
+ if (!(offset & 0x100))
+ {
+ if (!(offset & 0x01))
+ m_latch = data & 0x07;
+ else
+ {
+ m_reg[m_latch] = data;
+
+ switch (m_latch)
+ {
+ case 0x05:
+ prg32(data);
+ break;
+ case 0x07:
+ set_mirror(BIT(data, 0) ? 0 : (data >> 1) & 0x03);
+ break;
+ default:
+ chr_update();
+ break;
+ }
+ }
+ }
+}
+
+void nes_sachen_8259b_device::chr_update()
+{
+ UINT8 bank_helper1 = m_reg[7] & 0x01;
+ UINT8 bank_helper2 = (m_reg[4] & 0x07) << 3;
+
+ if (m_chr_source == CHRROM)
+ {
+ chr2_0(((m_reg[bank_helper1 ? 0 : 0] & 0x07) | bank_helper2), CHRROM);
+ chr2_2(((m_reg[bank_helper1 ? 0 : 1] & 0x07) | bank_helper2), CHRROM);
+ chr2_4(((m_reg[bank_helper1 ? 0 : 2] & 0x07) | bank_helper2), CHRROM);
+ chr2_6(((m_reg[bank_helper1 ? 0 : 3] & 0x07) | bank_helper2), CHRROM);
+ }
+}
+
+void nes_sachen_8259c_device::chr_update()
+{
+ UINT8 bank_helper1 = m_reg[7] & 0x01;
+ UINT8 bank_helper2 = (m_reg[4] & 0x07) << 3;
+
+ if (m_chr_source == CHRROM)
+ {
+ chr2_0(((m_reg[bank_helper1 ? 0 : 0] & 0x07) | bank_helper2) << 2 | 0, CHRROM);
+ chr2_2(((m_reg[bank_helper1 ? 0 : 1] & 0x07) | bank_helper2) << 2 | 1, CHRROM);
+ chr2_4(((m_reg[bank_helper1 ? 0 : 2] & 0x07) | bank_helper2) << 2 | 2, CHRROM);
+ chr2_6(((m_reg[bank_helper1 ? 0 : 3] & 0x07) | bank_helper2) << 2 | 3, CHRROM);
+ }
+}
+
+void nes_sachen_8259d_device::chr_update()
+{
+ if (m_chr_source == CHRROM)
+ {
+ chr1_0((m_reg[0] & 0x07), CHRROM);
+ chr1_1((m_reg[1] & 0x07) | (m_reg[4] << 4 & 0x10), CHRROM);
+ chr1_2((m_reg[2] & 0x07) | (m_reg[4] << 3 & 0x10), CHRROM);
+ chr1_3((m_reg[3] & 0x07) | (m_reg[4] << 2 & 0x10) | (m_reg[6] << 3 & 0x08), CHRROM);
+ }
+}
diff --git a/src/devices/bus/nes/sachen.h b/src/devices/bus/nes/sachen.h
new file mode 100644
index 00000000000..d6604578d3a
--- /dev/null
+++ b/src/devices/bus/nes/sachen.h
@@ -0,0 +1,261 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_SACHEN_H
+#define __NES_SACHEN_H
+
+#include "nxrom.h"
+
+
+// ======================> nes_sachen_sa009_device
+
+class nes_sachen_sa009_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sachen_sa009_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_sachen_sa0036_device
+
+class nes_sachen_sa0036_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sachen_sa0036_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_sachen_sa0037_device
+
+class nes_sachen_sa0037_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sachen_sa0037_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_sachen_sa72007_device
+
+class nes_sachen_sa72007_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sachen_sa72007_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_sachen_sa72008_device
+
+class nes_sachen_sa72008_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sachen_sa72008_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_sachen_tca01_device
+
+class nes_sachen_tca01_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sachen_tca01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_l);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_sachen_tcu01_device
+
+class nes_sachen_tcu01_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sachen_tcu01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+ virtual DECLARE_WRITE8_MEMBER(write_m) { write_l(space, (offset + 0x100) & 0xfff, data, mem_mask); }
+ virtual DECLARE_WRITE8_MEMBER(write_h) { write_l(space, (offset + 0x100) & 0xfff, data, mem_mask); }
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_sachen_tcu02_device
+
+class nes_sachen_tcu02_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sachen_tcu02_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_l);
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+};
+
+
+// ======================> nes_sachen_74x374_device
+
+class nes_sachen_74x374_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sachen_74x374_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_sachen_74x374_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_l);
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+
+ virtual void pcb_reset();
+
+protected:
+ void set_mirror(UINT8 nt);
+ UINT8 m_latch, m_mmc_vrom_bank;
+};
+
+
+// ======================> nes_sachen_74x374_alt_device
+
+class nes_sachen_74x374_alt_device : public nes_sachen_74x374_device
+{
+public:
+ // construction/destruction
+ nes_sachen_74x374_alt_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual DECLARE_READ8_MEMBER(read_l) { return 0xff; } // no read_l here
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+};
+
+
+// ======================> nes_sachen_8259a_device
+
+class nes_sachen_8259a_device : public nes_sachen_74x374_device
+{
+public:
+ // construction/destruction
+ nes_sachen_8259a_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_sachen_8259a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+ virtual DECLARE_WRITE8_MEMBER(write_m) { write_l(space, (offset + 0x100) & 0xfff, data, mem_mask); }
+
+ virtual void pcb_reset();
+
+protected:
+ virtual void chr_update();
+ UINT8 m_reg[8];
+};
+
+
+// ======================> nes_sachen_8259b_device
+
+class nes_sachen_8259b_device : public nes_sachen_8259a_device
+{
+public:
+ // construction/destruction
+ nes_sachen_8259b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void chr_update();
+};
+
+
+// ======================> nes_sachen_8259c_device
+
+class nes_sachen_8259c_device : public nes_sachen_8259a_device
+{
+public:
+ // construction/destruction
+ nes_sachen_8259c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+protected:
+ virtual void chr_update();
+};
+
+
+// ======================> nes_sachen_8259d_device
+
+class nes_sachen_8259d_device : public nes_sachen_8259a_device
+{
+public:
+ // construction/destruction
+ nes_sachen_8259d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual void pcb_reset();
+
+protected:
+ virtual void chr_update();
+};
+
+
+
+
+
+// device type definition
+extern const device_type NES_SACHEN_SA009;
+extern const device_type NES_SACHEN_SA0036;
+extern const device_type NES_SACHEN_SA0037;
+extern const device_type NES_SACHEN_SA72007;
+extern const device_type NES_SACHEN_SA72008;
+extern const device_type NES_SACHEN_TCA01;
+extern const device_type NES_SACHEN_TCU01;
+extern const device_type NES_SACHEN_TCU02;
+extern const device_type NES_SACHEN_74X374;
+extern const device_type NES_SACHEN_74X374_ALT;
+extern const device_type NES_SACHEN_8259A;
+extern const device_type NES_SACHEN_8259B;
+extern const device_type NES_SACHEN_8259C;
+extern const device_type NES_SACHEN_8259D;
+
+#endif
diff --git a/src/devices/bus/nes/somari.c b/src/devices/bus/nes/somari.c
new file mode 100644
index 00000000000..634aad58b68
--- /dev/null
+++ b/src/devices/bus/nes/somari.c
@@ -0,0 +1,421 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for Somari Team PCBs
+
+
+ Here we emulate the Somari Team PCBs [mapper 116]
+
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "somari.h"
+
+#include "cpu/m6502/m6502.h"
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+#define SOMARI_VRC2_MODE 0
+#define SOMARI_MMC3_MODE 1
+#define SOMARI_MMC1_MODE 2
+#define SOMARI_MMC1_MODE_AGAIN 3
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_SOMARI = &device_creator<nes_somari_device>;
+
+
+nes_somari_device::nes_somari_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_txrom_device(mconfig, NES_SOMARI, "NES Cart Team Somari PCB", tag, owner, clock, "nes_somari", __FILE__)
+{
+}
+
+
+
+void nes_somari_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_board_mode));
+
+ // MMC3
+ save_item(NAME(m_mmc_prg_bank));
+ save_item(NAME(m_mmc_vrom_bank));
+ save_item(NAME(m_latch));
+ save_item(NAME(m_prg_base));
+ save_item(NAME(m_prg_mask));
+ save_item(NAME(m_chr_base));
+ save_item(NAME(m_chr_mask));
+ save_item(NAME(m_mmc3_mirror_reg));
+
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_irq_count_latch));
+ save_item(NAME(m_irq_clear));
+
+ // MMC1
+ save_item(NAME(m_count));
+ save_item(NAME(m_mmc1_latch));
+ save_item(NAME(m_mmc1_reg));
+
+ // VRC2
+ save_item(NAME(m_vrc_prg_bank));
+ save_item(NAME(m_vrc_vrom_bank));
+ save_item(NAME(m_vrc_mirror_reg));
+}
+
+void nes_somari_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+
+ m_board_mode = 2; // mode
+
+ // MMC3
+ m_prg_base = m_chr_base = 0;
+ m_prg_mask = 0xff;
+ m_chr_mask = 0xff;
+ m_latch = 0;
+ m_mmc_prg_bank[0] = 0x3c;
+ m_mmc_prg_bank[1] = 0x3d;
+ m_mmc_prg_bank[2] = 0x3e;
+ m_mmc_prg_bank[3] = 0x3f;
+ m_mmc_vrom_bank[0] = 0x00;
+ m_mmc_vrom_bank[1] = 0x01;
+ m_mmc_vrom_bank[2] = 0x04;
+ m_mmc_vrom_bank[3] = 0x05;
+ m_mmc_vrom_bank[4] = 0x06;
+ m_mmc_vrom_bank[5] = 0x07;
+ m_mmc3_mirror_reg = 0;
+
+ m_alt_irq = 0;
+ m_irq_enable = 0;
+ m_irq_count = m_irq_count_latch = 0;
+ m_irq_clear = 0;
+
+ // MMC1 regs
+ m_count = 0;
+ m_mmc1_latch = 0;
+ m_mmc1_reg[0] = 0x0c;
+ m_mmc1_reg[1] = 0x00;
+ m_mmc1_reg[2] = 0x00;
+ m_mmc1_reg[3] = 0x00;
+
+ // VRC2 regs
+ m_vrc_prg_bank[0] = 0x00;
+ m_vrc_prg_bank[1] = 0x01;
+ for (int i = 0; i < 8; ++i)
+ m_vrc_vrom_bank[i] = i;
+ bank_update_switchmode();
+ m_vrc_mirror_reg = 0;
+}
+
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ SOMERI TEAM
+
+ iNES: mapper 116
+
+
+ Emulation note about regs in MESS: currently,
+ - m_mmc_prg_bank[n] for n=0,...,3 represent the MMC3 PRG banks (inherited from base class)
+ - m_mmc_vrom_bank[n] for n=0,...,5 represent the MMC3 CHR banks (inherited from base class)
+
+ - m_mmc1_reg[n] for n=0,1,2,3 represent the MMC1 regs
+ - m_count and m_mmc1_latch are additional variables for MMC1 (notice that MMC3 uses a diff m_latch!)
+
+ - m_vrc_prg_bank[n] for n=0,1 represent the VRC2 PRG banks
+ - m_vrc_vrom_bank[n] for n=0,...,7 represent the VRC2 CHR banks
+
+
+ In MESS: Preliminary support
+
+ -------------------------------------------------*/
+
+// MMC1 Mode emulation
+WRITE8_MEMBER(nes_somari_device::mmc1_w)
+{
+ assert(m_board_mode == 2);
+
+ if (data & 0x80)
+ {
+ m_count = 0;
+ m_mmc1_latch = 0;
+
+ m_mmc1_reg[0] |= 0x0c;
+ update_prg();
+ return;
+ }
+
+ if (m_count < 5)
+ {
+ if (m_count == 0) m_mmc1_latch = 0;
+ m_mmc1_latch >>= 1;
+ m_mmc1_latch |= (data & 0x01) ? 0x10 : 0x00;
+ m_count++;
+ }
+
+ if (m_count == 5)
+ {
+ m_mmc1_reg[(offset & 0x6000) >> 13] = m_mmc1_latch;
+ update_mirror();
+ update_prg();
+ update_chr();
+
+ m_count = 0;
+ }
+}
+
+// MMC3 Mode emulation
+WRITE8_MEMBER(nes_somari_device::mmc3_w)
+{
+ UINT8 mmc_helper, cmd;
+
+ assert(m_board_mode == 1);
+
+ switch (offset & 0x6001)
+ {
+ case 0x0000:
+ mmc_helper = m_latch ^ data;
+ m_latch = data;
+
+ if (mmc_helper & 0x40)
+ update_prg();
+
+ if (mmc_helper & 0x80)
+ update_chr();
+ break;
+
+ case 0x0001:
+ cmd = m_latch & 0x07;
+ switch (cmd)
+ {
+ case 0: case 1:
+ case 2: case 3: case 4: case 5:
+ m_mmc_vrom_bank[cmd] = data;
+ update_chr();
+ break;
+ case 6:
+ case 7:
+ m_mmc_prg_bank[cmd - 6] = data & 0x3f;
+ update_prg();
+ break;
+ }
+ break;
+
+ case 0x2000:
+ m_mmc3_mirror_reg = data & 1;
+ update_mirror();
+ break;
+ case 0x2001: break;
+ case 0x4000: m_irq_count_latch = data; break;
+ case 0x4001: m_irq_count = 0; break;
+ case 0x6000: m_irq_enable = 0; m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE); break;
+ case 0x6001: m_irq_enable = 1; break;
+ }
+}
+
+// VRC2 Mode emulation
+WRITE8_MEMBER(nes_somari_device::vrc2_w)
+{
+ UINT8 bank, shift;
+
+ assert(m_board_mode == 0);
+
+ switch (offset & 0x7000)
+ {
+ case 0x0000:
+ m_vrc_prg_bank[0] = data & 0x1f;
+ update_prg();
+ break;
+
+ case 0x1000:
+ m_vrc_mirror_reg = data & 1;
+ update_mirror();
+ break;
+
+ case 0x2000:
+ m_vrc_prg_bank[1] = data & 0x1f;
+ update_prg();
+ break;
+
+ case 0x3000:
+ case 0x4000:
+ case 0x5000:
+ case 0x6000:
+ // this makes no sense for vrc2 and breaks somari, but it's ok for garousp!!
+ bank = ((offset & 0x7000) - 0x3000) / 0x0800 + BIT(offset, 1);
+ shift = BIT(offset, 2) * 4;
+ data = (data & 0x0f) << shift;
+ m_vrc_vrom_bank[bank] = data;
+
+ update_chr();
+ break;
+ }
+}
+
+
+void nes_somari_device::update_prg()
+{
+ switch (m_board_mode)
+ {
+ case SOMARI_VRC2_MODE:
+ prg8_89(m_vrc_prg_bank[0]);
+ prg8_ab(m_vrc_prg_bank[1]);
+ prg8_cd(0x3e);
+ prg8_ef(0x3f);
+ break;
+ case SOMARI_MMC3_MODE:
+ {
+ UINT8 prg_flip = (m_latch & 0x40) ? 2 : 0;
+ prg8_x(0, m_mmc_prg_bank[0 ^ prg_flip]);
+ prg8_x(1, m_mmc_prg_bank[1]);
+ prg8_x(2, m_mmc_prg_bank[2 ^ prg_flip]);
+ prg8_x(3, m_mmc_prg_bank[3]);
+ }
+ break;
+ case SOMARI_MMC1_MODE:
+// case SOMARI_MMC1_MODE_AGAIN:
+ {
+ UINT8 prg_offset = m_mmc1_reg[1] & 0x10;
+
+ switch (m_mmc1_reg[0] & 0x0c)
+ {
+ case 0x00:
+ case 0x04:
+ prg32((prg_offset + m_mmc1_reg[3]) >> 1);
+ break;
+ case 0x08:
+ prg16_89ab(prg_offset + 0);
+ prg16_cdef(prg_offset + m_mmc1_reg[3]);
+ break;
+ case 0x0c:
+ prg16_89ab(prg_offset + m_mmc1_reg[3]);
+ prg16_cdef(prg_offset + 0x0f);
+ break;
+ }
+ }
+ break;
+ }
+}
+
+void nes_somari_device::update_chr()
+{
+ switch (m_board_mode)
+ {
+ case SOMARI_VRC2_MODE:
+ for (int i = 0; i < 8; i++)
+ chr1_x(i, m_chr_base | m_vrc_vrom_bank[i], CHRROM);
+ break;
+ case SOMARI_MMC3_MODE:
+ {
+ UINT8 chr_page = (m_latch & 0x80) >> 5;
+ chr1_x(chr_page ^ 0, m_chr_base | ((m_mmc_vrom_bank[0] & ~0x01)), CHRROM);
+ chr1_x(chr_page ^ 1, m_chr_base | ((m_mmc_vrom_bank[0] | 0x01)), CHRROM);
+ chr1_x(chr_page ^ 2, m_chr_base | ((m_mmc_vrom_bank[1] & ~0x01)), CHRROM);
+ chr1_x(chr_page ^ 3, m_chr_base | ((m_mmc_vrom_bank[1] | 0x01)), CHRROM);
+ chr1_x(chr_page ^ 4, m_chr_base | (m_mmc_vrom_bank[2]), CHRROM);
+ chr1_x(chr_page ^ 5, m_chr_base | (m_mmc_vrom_bank[3]), CHRROM);
+ chr1_x(chr_page ^ 6, m_chr_base | (m_mmc_vrom_bank[4]), CHRROM);
+ chr1_x(chr_page ^ 7, m_chr_base | (m_mmc_vrom_bank[5]), CHRROM);
+ }
+ break;
+ case SOMARI_MMC1_MODE:
+// case SOMARI_MMC1_MODE_AGAIN:
+ if (BIT(m_mmc1_reg[0], 4))
+ {
+ chr4_0(m_mmc1_reg[1] & 0x1f, CHRROM);
+ chr4_4(m_mmc1_reg[2] & 0x1f, CHRROM);
+ }
+ else
+ chr8((m_mmc1_reg[1] & 0x1f) >> 1, CHRROM);
+ break;
+ }
+}
+
+void nes_somari_device::update_mirror()
+{
+ switch (m_board_mode)
+ {
+ case SOMARI_VRC2_MODE:
+ set_nt_mirroring(m_vrc_mirror_reg ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ break;
+ case SOMARI_MMC3_MODE:
+ set_nt_mirroring(m_mmc3_mirror_reg ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ break;
+ case SOMARI_MMC1_MODE:
+// case SOMARI_MMC1_MODE_AGAIN:
+ switch (m_mmc1_reg[0] & 0x03)
+ {
+ case 0x00: set_nt_mirroring(PPU_MIRROR_LOW); break;
+ case 0x01: set_nt_mirroring(PPU_MIRROR_HIGH); break;
+ case 0x02: set_nt_mirroring(PPU_MIRROR_VERT); break;
+ case 0x03: set_nt_mirroring(PPU_MIRROR_HORZ); break;
+ }
+ break;
+ }
+}
+
+
+WRITE8_MEMBER(nes_somari_device::write_h)
+{
+ LOG_MMC(("somari write_h, mode %d, offset: %04x, data: %02x\n", m_board_mode, offset, data));
+
+ switch (m_board_mode)
+ {
+ case SOMARI_VRC2_MODE: vrc2_w(space, offset, data, mem_mask); break;
+ case SOMARI_MMC3_MODE: mmc3_w(space, offset, data, mem_mask); break;
+ case SOMARI_MMC1_MODE: mmc1_w(space, offset, data, mem_mask); break;
+ }
+}
+
+void nes_somari_device::bank_update_switchmode()
+{
+ switch (m_board_mode)
+ {
+ case SOMARI_VRC2_MODE:
+ break;
+ case SOMARI_MMC3_MODE:
+ break;
+ case SOMARI_MMC1_MODE:
+ break;
+ }
+ update_mirror();
+ update_prg();
+ update_chr();
+}
+
+WRITE8_MEMBER(nes_somari_device::write_m)
+{
+ LOG_MMC(("somari write_m, offset: %04x, data: %02x\n", offset, data));
+
+ if (offset & 0x100)
+ {
+ m_board_mode = data & 0x03;
+ m_chr_base = ((m_board_mode & 0x04) << 6);
+ if (m_board_mode != 1)
+ m_irq_enable = 0;
+ bank_update_switchmode();
+ }
+}
diff --git a/src/devices/bus/nes/somari.h b/src/devices/bus/nes/somari.h
new file mode 100644
index 00000000000..726df0a4926
--- /dev/null
+++ b/src/devices/bus/nes/somari.h
@@ -0,0 +1,54 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_SOMARI_H
+#define __NES_SOMARI_H
+
+#include "mmc3.h"
+
+
+// ======================> nes_somari_device
+
+class nes_somari_device : public nes_txrom_device
+{
+public:
+ // construction/destruction
+ nes_somari_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l) { write_m(space, offset + 0x100, data, mem_mask); }
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(mmc1_w);
+ virtual DECLARE_WRITE8_MEMBER(mmc3_w);
+ virtual DECLARE_WRITE8_MEMBER(vrc2_w);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+private:
+ void update_prg();
+ void update_chr();
+ void update_mirror();
+ void bank_update_switchmode();
+
+ UINT8 m_board_mode;
+
+ // MMC3 - inherited from txrom
+ UINT8 m_mmc3_mirror_reg;
+
+ // MMC1
+ UINT8 m_count;
+ UINT8 m_mmc1_latch;
+ UINT8 m_mmc1_reg[4];
+
+ // VRC2
+ UINT8 m_vrc_prg_bank[2];
+ UINT8 m_vrc_vrom_bank[8];
+ UINT8 m_vrc_mirror_reg;
+};
+
+
+
+// device type definition
+extern const device_type NES_SOMARI;
+
+#endif
diff --git a/src/devices/bus/nes/sunsoft.c b/src/devices/bus/nes/sunsoft.c
new file mode 100644
index 00000000000..48d95c05727
--- /dev/null
+++ b/src/devices/bus/nes/sunsoft.c
@@ -0,0 +1,628 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for Sunsoft PCBs
+
+
+ Here we emulate the following PCBs
+
+ * Sunsoft-1 [mapper 184]
+ * Sunsoft-2 [mapper 89 & 93]
+ * Sunsoft-3 [mapper 67]
+ * Sunsoft-4 [mapper 68]
+ * Sunsoft FME7 & Sunsoft-5A [mapper 69]
+ * Sunsoft-5B [mapper 69]
+
+ TODO:
+ - check 1-line glitches due to IRQ in Sunsoft-3
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "sunsoft.h"
+
+#include "cpu/m6502/m6502.h"
+#include "sound/ay8910.h"
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_SUNSOFT_1 = &device_creator<nes_sunsoft_1_device>;
+const device_type NES_SUNSOFT_2 = &device_creator<nes_sunsoft_2_device>;
+const device_type NES_SUNSOFT_3 = &device_creator<nes_sunsoft_3_device>;
+const device_type NES_SUNSOFT_4 = &device_creator<nes_sunsoft_4_device>;
+const device_type NES_SUNSOFT_FME7 = &device_creator<nes_sunsoft_fme7_device>;
+const device_type NES_SUNSOFT_5 = &device_creator<nes_sunsoft_5_device>;
+
+
+nes_sunsoft_1_device::nes_sunsoft_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SUNSOFT_1, "NES Cart Sunsoft 1 PCB", tag, owner, clock, "nes_sun1", __FILE__)
+{
+}
+
+nes_sunsoft_2_device::nes_sunsoft_2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SUNSOFT_2, "NES Cart Sunsoft 2 PCB", tag, owner, clock, "nes_sun2", __FILE__)
+{
+}
+
+nes_sunsoft_3_device::nes_sunsoft_3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SUNSOFT_3, "NES Cart Sunsoft 3 PCB", tag, owner, clock, "nes_sun3", __FILE__)
+{
+}
+
+nes_sunsoft_4_device::nes_sunsoft_4_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : nes_nrom_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+nes_sunsoft_4_device::nes_sunsoft_4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SUNSOFT_4, "NES Cart Sunsoft 4 PCB", tag, owner, clock, "nes_sun4", __FILE__)
+{
+}
+
+nes_sunsoft_fme7_device::nes_sunsoft_fme7_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : nes_nrom_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+nes_sunsoft_fme7_device::nes_sunsoft_fme7_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_SUNSOFT_4, "NES Cart Sunsoft FME7 PCB", tag, owner, clock, "nes_fme7", __FILE__)
+{
+}
+
+nes_sunsoft_5_device::nes_sunsoft_5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_sunsoft_fme7_device(mconfig, NES_SUNSOFT_5, "NES Cart Sunsoft 5A/5B PCB", tag, owner, clock, "nes_sun5", __FILE__),
+ m_ym2149(*this, "ay")
+{
+}
+
+
+void nes_sunsoft_1_device::device_start()
+{
+ common_start();
+}
+
+void nes_sunsoft_1_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+}
+
+void nes_sunsoft_2_device::device_start()
+{
+ common_start();
+}
+
+void nes_sunsoft_2_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+ if (m_pcb_ctrl_mirror)
+ set_nt_mirroring(PPU_MIRROR_LOW);
+}
+
+void nes_sunsoft_3_device::device_start()
+{
+ common_start();
+ irq_timer = timer_alloc(TIMER_IRQ);
+ irq_timer->adjust(attotime::zero, 0, machine().device<cpu_device>("maincpu")->cycles_to_attotime(1));
+
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_toggle));
+ save_item(NAME(m_irq_count));
+}
+
+void nes_sunsoft_3_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+ m_irq_toggle = 0;
+ m_irq_count = 0;
+ m_irq_enable = 0;
+}
+
+void nes_sunsoft_4_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch1));
+ save_item(NAME(m_latch2));
+ save_item(NAME(m_reg));
+ save_item(NAME(m_wram_enable));
+}
+
+void nes_sunsoft_4_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_reg = 0;
+ m_latch1 = 0;
+ m_latch2 = 0;
+ m_wram_enable = 0;
+}
+
+void nes_sunsoft_fme7_device::device_start()
+{
+ common_start();
+ irq_timer = timer_alloc(TIMER_IRQ);
+ // this has to be hardcoded because some some scanline code only suits NTSC... it will be fixed with PPU rewrite
+ irq_timer->adjust(attotime::zero, 0, attotime::from_hz((21477272.724 / 12)));
+// irq_timer->adjust(attotime::zero, 0, machine().device<cpu_device>("maincpu")->cycles_to_attotime(1));
+
+ save_item(NAME(m_wram_bank));
+ save_item(NAME(m_latch));
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_count));
+}
+
+void nes_sunsoft_fme7_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_wram_bank = 0;
+
+ m_latch = 0;
+ m_irq_enable = 0;
+ m_irq_count = 0;
+}
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ Sunsoft-1 board emulation
+
+ Games: Atlantis no Nazo, Kanshakudama Nage Kantarou no
+ Toukaidou Gojuusan Tsugi, Wing of Madoola, Fantasy Zone
+
+ iNES: mapper 184 (Fantasy Zone uses this board with no
+ CHRROM, and the register switches PRG banks)
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_sunsoft_1_device::write_m)
+{
+ LOG_MMC(("Sunsoft 1 write_m, offset: %04x, data: %02x\n", offset, data));
+
+ if (m_vrom_chunks)
+ {
+ chr4_0(data & 0x0f, CHRROM);
+ chr4_4(data >> 4, CHRROM);
+ }
+ else
+ prg16_89ab(data & 0x0f);
+}
+
+/*-------------------------------------------------
+
+ Sunsoft-2 board emulation
+
+ The two games using this board have incompatible mirroring
+ wiring, making necessary two distinct mappers
+
+ iNES: mapper 89 & 93
+
+ -------------------------------------------------*/
+
+// there are two 'variants' depending on hardwired or mapper ctrl mirroring
+
+WRITE8_MEMBER(nes_sunsoft_2_device::write_h)
+{
+ UINT8 helper = (data & 0x07) | ((data & 0x80) ? 0x08 : 0x00);
+ LOG_MMC(("Sunsoft 2 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ // this pcb is subject to bus conflict
+ data = account_bus_conflict(offset, data);
+
+ if (m_pcb_ctrl_mirror)
+ set_nt_mirroring(BIT(data, 3) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW);
+
+ if (m_vrom_chunks)
+ chr8(helper, CHRROM);
+
+ prg16_89ab(data >> 4);
+}
+
+/*-------------------------------------------------
+
+ Sunsoft-3 board emulation
+
+ The two games using this board have incompatible mirroring
+ wiring, making necessary two distinct mappers & pcb_id
+
+ iNES: mapper 67
+
+ -------------------------------------------------*/
+
+
+void nes_sunsoft_3_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if (id == TIMER_IRQ)
+ {
+ if (m_irq_enable)
+ {
+ if (!m_irq_count)
+ {
+ m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
+ m_irq_count = 0xffff;
+ m_irq_enable = 0;
+ }
+ else
+ m_irq_count--;
+ }
+ }
+}
+
+WRITE8_MEMBER(nes_sunsoft_3_device::write_h)
+{
+ LOG_MMC(("Sunsoft 3 write_h, offset %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x7800)
+ {
+ case 0x0800:
+ chr2_0(data, CHRROM);
+ break;
+ case 0x1800:
+ chr2_2(data, CHRROM);
+ break;
+ case 0x2800:
+ chr2_4(data, CHRROM);
+ break;
+ case 0x3800:
+ chr2_6(data, CHRROM);
+ break;
+ case 0x4000:
+ case 0x4800:
+ m_irq_toggle ^= 1;
+ if (m_irq_toggle)
+ m_irq_count = (m_irq_count & 0x00ff) | (data << 8);
+ else
+ m_irq_count = (m_irq_count & 0xff00) | data;
+ break;
+ case 0x5800:
+ m_irq_enable = BIT(data, 4);
+ m_irq_toggle = 0;
+ m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
+ break;
+ case 0x6800:
+ switch (data & 3)
+ {
+ case 0x00: set_nt_mirroring(PPU_MIRROR_VERT); break;
+ case 0x01: set_nt_mirroring(PPU_MIRROR_HORZ); break;
+ case 0x02: set_nt_mirroring(PPU_MIRROR_LOW); break;
+ case 0x03: set_nt_mirroring(PPU_MIRROR_HIGH); break;
+ }
+ break;
+ case 0x7800:
+ prg16_89ab(data);
+ break;
+ default:
+ LOG_MMC(("Sunsoft 3 write_h uncaught write, offset: %04x, data: %02x\n", offset, data));
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Sunsoft-4 board emulation
+
+ Games: AfterBurner & Maharaja
+
+ iNES: mapper 68
+
+ -------------------------------------------------*/
+
+void nes_sunsoft_4_device::sun4_mirror( int mirror, int mirr0, int mirr1 )
+{
+ switch (mirror)
+ {
+ case 0x00:
+ set_nt_mirroring(PPU_MIRROR_VERT);
+ break;
+ case 0x01:
+ set_nt_mirroring(PPU_MIRROR_HORZ);
+ break;
+ case 0x02:
+ set_nt_mirroring(PPU_MIRROR_LOW);
+ break;
+ case 0x03:
+ set_nt_mirroring(PPU_MIRROR_HIGH);
+ break;
+ case 0x10:
+ set_nt_page(0, VROM, mirr0 | 0x80, 0);
+ set_nt_page(1, VROM, mirr1 | 0x80, 0);
+ set_nt_page(2, VROM, mirr0 | 0x80, 0);
+ set_nt_page(3, VROM, mirr1 | 0x80, 0);
+ break;
+ case 0x11:
+ set_nt_page(0, VROM, mirr0 | 0x80, 0);
+ set_nt_page(1, VROM, mirr0 | 0x80, 0);
+ set_nt_page(2, VROM, mirr1 | 0x80, 0);
+ set_nt_page(3, VROM, mirr1 | 0x80, 0);
+ break;
+ case 0x12:
+ set_nt_page(0, VROM, mirr0 | 0x80, 0);
+ set_nt_page(1, VROM, mirr0 | 0x80, 0);
+ set_nt_page(2, VROM, mirr0 | 0x80, 0);
+ set_nt_page(3, VROM, mirr0 | 0x80, 0);
+ break;
+ case 0x13:
+ set_nt_page(0, VROM, mirr1 | 0x80, 0);
+ set_nt_page(1, VROM, mirr1 | 0x80, 0);
+ set_nt_page(2, VROM, mirr1 | 0x80, 0);
+ set_nt_page(3, VROM, mirr1 | 0x80, 0);
+ break;
+ }
+}
+
+WRITE8_MEMBER(nes_sunsoft_4_device::sun4_write)
+{
+ LOG_MMC(("Sunsoft 4 write_h, offset %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x7000)
+ {
+ case 0x0000:
+ chr2_0(data, CHRROM);
+ break;
+ case 0x1000:
+ chr2_2(data, CHRROM);
+ break;
+ case 0x2000:
+ chr2_4(data, CHRROM);
+ break;
+ case 0x3000:
+ chr2_6(data, CHRROM);
+ break;
+ case 0x4000:
+ m_latch1 = data & 0x7f;
+ sun4_mirror(m_reg, m_latch1, m_latch2);
+ break;
+ case 0x5000:
+ m_latch2 = data & 0x7f;
+ sun4_mirror(m_reg, m_latch1, m_latch2);
+ break;
+ case 0x6000:
+ m_reg = data & 0x13;
+ sun4_mirror(m_reg, m_latch1, m_latch2);
+ break;
+ case 0x7000:
+ prg16_89ab(data & 0x0f);
+ m_wram_enable = BIT(data, 4);
+ break;
+ default:
+ LOG_MMC(("Sunsoft 4 write_h uncaught write, offset: %04x, data: %02x\n", offset, data));
+ break;
+ }
+}
+
+WRITE8_MEMBER(nes_sunsoft_4_device::write_m)
+{
+ LOG_MMC(("Sunsoft 4 write_m, offset: %04x, data: %02x\n", offset, data));
+
+ if (!m_battery.empty() && m_wram_enable)
+ m_battery[offset & (m_battery.size() - 1)] = data;
+ if (!m_prgram.empty() && m_wram_enable)
+ m_prgram[offset & (m_prgram.size() - 1)] = data;
+}
+
+READ8_MEMBER(nes_sunsoft_4_device::read_m)
+{
+ LOG_MMC(("Sunsoft 4 read_m, offset: %04x\n", offset));
+
+ if (!m_battery.empty() && m_wram_enable)
+ return m_battery[offset & (m_battery.size() - 1)];
+ if (!m_prgram.empty() && m_wram_enable)
+ return m_prgram[offset & (m_prgram.size() - 1)];
+
+ return m_open_bus; // open bus
+}
+
+/*-------------------------------------------------
+
+ JxROM & Sunsoft 5A / 5B / FME7 board emulation
+
+ Notice that Sunsoft-5B = FME7 + sound chip (the latter being
+ currently unemulated in MESS)
+
+ iNES: mapper 69
+
+ -------------------------------------------------*/
+
+void nes_sunsoft_fme7_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if (id == TIMER_IRQ)
+ {
+ if ((m_irq_enable & 0x80)) // bit7, counter decrement
+ {
+ if (!m_irq_count)
+ {
+ m_irq_count = 0xffff;
+ if (m_irq_enable & 0x01) // bit0, trigger enable
+ m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
+ }
+ else
+ m_irq_count--;
+ }
+ }
+}
+
+WRITE8_MEMBER(nes_sunsoft_fme7_device::fme7_write)
+{
+ LOG_MMC(("fme7_write, offset %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x6000)
+ {
+ case 0x0000:
+ m_latch = data & 0x0f;
+ break;
+
+ case 0x2000:
+ switch (m_latch)
+ {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ chr1_x(m_latch, data, CHRROM);
+ break;
+
+ case 8:
+ m_wram_bank = data;
+ break;
+ case 9:
+ prg8_89(data);
+ break;
+ case 0x0a:
+ prg8_ab(data);
+ break;
+ case 0x0b:
+ prg8_cd(data);
+ break;
+ case 0x0c:
+ switch (data & 0x03)
+ {
+ case 0x00: set_nt_mirroring(PPU_MIRROR_VERT); break;
+ case 0x01: set_nt_mirroring(PPU_MIRROR_HORZ); break;
+ case 0x02: set_nt_mirroring(PPU_MIRROR_LOW); break;
+ case 0x03: set_nt_mirroring(PPU_MIRROR_HIGH); break;
+ }
+ break;
+ case 0x0d:
+ m_irq_enable = data;
+ if (!(m_irq_enable & 1))
+ m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
+ break;
+ case 0x0e:
+ m_irq_count = (m_irq_count & 0xff00) | data;
+ break;
+ case 0x0f:
+ m_irq_count = (m_irq_count & 0x00ff) | (data << 8);
+ break;
+ }
+ break;
+
+ default:
+ logerror("Sunsoft FME7 write_h uncaught %04x value: %02x\n", offset + 0x8000, data);
+ break;
+ }
+}
+
+WRITE8_MEMBER(nes_sunsoft_fme7_device::write_m)
+{
+ UINT8 bank = m_wram_bank & 0x3f;
+ LOG_MMC(("Sunsoft FME7 write_m, offset: %04x, data: %02x\n", offset, data));
+
+ if (!(m_wram_bank & 0x40)) // is PRG ROM, no write
+ return;
+ else if (m_wram_bank & 0x80) // is PRG RAM
+ {
+ if (!m_battery.empty())
+ m_battery[((bank * 0x2000) + offset) & (m_battery.size() - 1)] = data;
+ if (!m_prgram.empty())
+ m_prgram[((bank * 0x2000) + offset) & (m_prgram.size() - 1)] = data;
+ }
+}
+
+READ8_MEMBER(nes_sunsoft_fme7_device::read_m)
+{
+ UINT8 bank = m_wram_bank & 0x3f;
+ LOG_MMC(("Sunsoft FME7 read_m, offset: %04x\n", offset));
+
+ if (!(m_wram_bank & 0x40)) // is PRG ROM
+ return m_prg[((bank * 0x2000) + offset) & (m_prg_size - 1)];
+ else if (m_wram_bank & 0x80) // is PRG RAM
+ {
+ if (!m_battery.empty())
+ return m_battery[((bank * 0x2000) + offset) & (m_battery.size() - 1)];
+ if (!m_prgram.empty())
+ return m_prgram[((bank * 0x2000) + offset) & (m_prgram.size() - 1)];
+ }
+
+ return m_open_bus; // open bus
+}
+
+
+/*-------------------------------------------------
+
+ Sunsoft 5B board emulation (FME7 + Sound)
+
+ Games: Gimmick!
+
+ iNES: mapper 69
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_sunsoft_5_device::write_h)
+{
+ LOG_MMC(("sunsoft 5 write_h, offset %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x6000)
+ {
+ case 0x4000:
+ m_ym2149->address_w(space, 0, data & 0x0f);
+ break;
+ case 0x6000:
+ m_ym2149->data_w(space, 0, data);
+ break;
+ default:
+ fme7_write(space, offset, data, mem_mask);
+ break;
+ }
+}
+
+//-------------------------------------------------
+// MACHINE_DRIVER( sun_5b )
+//-------------------------------------------------
+
+// From NESdev wiki: The 5B's audio is driven by the CPU clock (1.78977267 MHz),
+// but like the NES's APU, the YM2149F has an optional clock divider which
+// halves the internal clock speed. By comparison of the produced pitches
+// in Gimmick! with the register values used, it appears that the 5B is a
+// YM2149F operating in this mode. To use an AY-3-8910 as a substitute,
+// you would need an external divider to reduce the clock speed by half.
+
+#define SUN5_NTSC_CLOCK (21477272.724 / 12)
+
+static MACHINE_CONFIG_FRAGMENT( sun_5b )
+
+ // additional sound hardware
+ MCFG_SPEAKER_STANDARD_MONO("addon")
+
+ MCFG_SOUND_ADD("ay", YM2149, SUN5_NTSC_CLOCK/2) // divide by 2 for the internal divider
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "addon", 0.50)
+MACHINE_CONFIG_END
+
+//-------------------------------------------------
+// machine_config_additions - device-specific
+// machine configurations
+//-------------------------------------------------
+
+machine_config_constructor nes_sunsoft_5_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( sun_5b );
+}
diff --git a/src/devices/bus/nes/sunsoft.h b/src/devices/bus/nes/sunsoft.h
new file mode 100644
index 00000000000..53ea24adeae
--- /dev/null
+++ b/src/devices/bus/nes/sunsoft.h
@@ -0,0 +1,147 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_SUNSOFT_H
+#define __NES_SUNSOFT_H
+
+#include "nxrom.h"
+#include "sound/ay8910.h"
+
+
+// ======================> nes_sunsoft_1_device
+
+class nes_sunsoft_1_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sunsoft_1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_sunsoft_2_device
+
+class nes_sunsoft_2_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sunsoft_2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_sunsoft_3_device
+
+class nes_sunsoft_3_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sunsoft_3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ UINT16 m_irq_count;
+ int m_irq_enable, m_irq_toggle;
+
+ static const device_timer_id TIMER_IRQ = 0;
+ emu_timer *irq_timer;
+};
+
+
+// ======================> nes_sunsoft_4_device
+
+class nes_sunsoft_4_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sunsoft_4_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_sunsoft_4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(sun4_write);
+ virtual DECLARE_WRITE8_MEMBER(write_h) { sun4_write(space, offset, data, mem_mask); }
+
+ virtual void pcb_reset();
+
+protected:
+ void sun4_mirror(int mirror, int mirr0, int mirr1);
+ int m_reg, m_latch1, m_latch2, m_wram_enable;
+};
+
+// ======================> nes_sunsoft_fme7_device
+
+class nes_sunsoft_fme7_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_sunsoft_fme7_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_sunsoft_fme7_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(fme7_write);
+ virtual DECLARE_WRITE8_MEMBER(write_h) { fme7_write(space, offset, data, mem_mask); }
+
+ virtual void pcb_reset();
+
+private:
+ UINT16 m_irq_count;
+ int m_irq_enable;
+
+ static const device_timer_id TIMER_IRQ = 0;
+ emu_timer *irq_timer;
+
+ UINT8 m_latch;
+ UINT8 m_wram_bank;
+};
+
+
+// ======================> nes_sunsoft_5_device
+
+class nes_sunsoft_5_device : public nes_sunsoft_fme7_device
+{
+public:
+ // construction/destruction
+ nes_sunsoft_5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual machine_config_constructor device_mconfig_additions() const;
+
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+private:
+ required_device<ay8910_device> m_ym2149;
+};
+
+
+
+
+// device type definition
+extern const device_type NES_SUNSOFT_1;
+extern const device_type NES_SUNSOFT_2;
+extern const device_type NES_SUNSOFT_3;
+extern const device_type NES_SUNSOFT_4;
+extern const device_type NES_SUNSOFT_FME7;
+extern const device_type NES_SUNSOFT_5;
+
+#endif
diff --git a/src/devices/bus/nes/sunsoft_dcs.c b/src/devices/bus/nes/sunsoft_dcs.c
new file mode 100644
index 00000000000..c7de9fd3040
--- /dev/null
+++ b/src/devices/bus/nes/sunsoft_dcs.c
@@ -0,0 +1,315 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for Sunsoft DCS PCBs
+
+
+ Here we emulate the Sunsoft Dual Cassette System (DCS) PCB used by Nantettate!! Baseball + subslot
+
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "sunsoft_dcs.h"
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-----------------------------------------------
+//
+// Nantettate!! Baseball Cartslot implementation
+//
+//-----------------------------------------------
+
+//-------------------------------------------------
+// sub-cart interface
+//-------------------------------------------------
+
+ntb_cart_interface::ntb_cart_interface(const machine_config &mconfig, device_t &device)
+ : device_slot_card_interface(mconfig, device),
+ m_rom(NULL)
+{
+}
+
+ntb_cart_interface::~ntb_cart_interface()
+{
+}
+
+//-------------------------------------------------
+// sub-cart slot device
+//-------------------------------------------------
+
+const device_type NES_NTB_SLOT = &device_creator<nes_ntb_slot_device>;
+
+nes_ntb_slot_device::nes_ntb_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
+ device_t(mconfig, NES_NTB_SLOT, "NES NTB Cartridge Slot", tag, owner, clock, "nes_ntb_slot", __FILE__),
+ device_image_interface(mconfig, *this),
+ device_slot_interface(mconfig, *this)
+{
+}
+
+nes_ntb_slot_device::~nes_ntb_slot_device()
+{
+}
+
+
+void nes_ntb_slot_device::device_start()
+{
+ m_cart = dynamic_cast<ntb_cart_interface *>(get_card_device());
+}
+
+READ8_MEMBER(nes_ntb_slot_device::read)
+{
+ if (m_cart)
+ return m_cart->read(space, offset, mem_mask);
+
+ return 0xff;
+}
+
+
+bool nes_ntb_slot_device::call_load()
+{
+ if (m_cart)
+ {
+ UINT8 *ROM = m_cart->get_cart_base();
+
+ if (!ROM)
+ return IMAGE_INIT_FAIL;
+
+ if (software_entry() == NULL)
+ {
+ if (length() != 0x4000)
+ return IMAGE_INIT_FAIL;
+
+ fread(&ROM, 0x4000);
+ }
+ else
+ {
+ if (get_software_region_length("rom") != 0x4000)
+ return IMAGE_INIT_FAIL;
+
+ memcpy(ROM, get_software_region("rom"), 0x4000);
+ }
+ }
+
+ return IMAGE_INIT_PASS;
+}
+
+
+bool nes_ntb_slot_device::call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry)
+{
+ load_software_part_region(*this, swlist, swname, start_entry );
+ return TRUE;
+}
+
+void nes_ntb_slot_device::get_default_card_software(std::string &result)
+{
+ software_get_default_slot(result, "ntbrom");
+}
+
+//-----------------------------------------------
+//
+// Nantettate!! Baseball Minicart implementation
+//
+//-----------------------------------------------
+
+ROM_START( ntb_rom )
+ ROM_REGION(0x4000, "ntbrom", ROMREGION_ERASEFF)
+ROM_END
+
+const device_type NES_NTB_ROM = &device_creator<nes_ntb_rom_device>;
+
+nes_ntb_rom_device::nes_ntb_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, NES_NTB_ROM, "NES NTB ROM", tag, owner, clock, "nes_ntbrom", __FILE__),
+ ntb_cart_interface( mconfig, *this )
+{
+}
+
+void nes_ntb_rom_device::device_start()
+{
+ m_rom = (UINT8*)memregion("ntbrom")->base();
+}
+
+const rom_entry *nes_ntb_rom_device::device_rom_region() const
+{
+ return ROM_NAME( ntb_rom );
+}
+
+UINT8 *nes_ntb_rom_device::get_cart_base()
+{
+ return m_rom;
+}
+
+
+
+//------------------------------------------------
+//
+// Nantettate!! Baseball base cart implementation
+// a.k.a. Sunsoft Dual Cassette System
+// (variant of Sunsoft-4 PCB)
+//
+//------------------------------------------------
+
+const device_type NES_SUNSOFT_DCS = &device_creator<nes_sunsoft_dcs_device>;
+
+
+nes_sunsoft_dcs_device::nes_sunsoft_dcs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_sunsoft_4_device(mconfig, NES_SUNSOFT_DCS, "NES Cart Sunsoft DCS PCB", tag, owner, clock, "nes_dcs", __FILE__),
+ m_subslot(*this, "ntb_slot")
+{
+}
+
+
+void nes_sunsoft_dcs_device::device_start()
+{
+ common_start();
+ ntb_enable_timer = timer_alloc(TIMER_PROTECT);
+ ntb_enable_timer->reset();
+ timer_freq = machine().device<cpu_device>("maincpu")->cycles_to_attotime(107520);
+
+ save_item(NAME(m_latch1));
+ save_item(NAME(m_latch2));
+ save_item(NAME(m_reg));
+ save_item(NAME(m_wram_enable));
+ save_item(NAME(m_exrom_enable));
+ save_item(NAME(m_timer_on));
+}
+
+void nes_sunsoft_dcs_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_reg = 0;
+ m_latch1 = 0;
+ m_latch2 = 0;
+ m_wram_enable = 0;
+ m_exrom_enable = 0;
+ m_timer_on = 0;
+}
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ Sunsoft-DCS board emulation
+
+ Games: Nantettatte!! Baseball
+
+ This board feature a subslot to load minicart containing
+ updated data?
+
+ iNES: mapper 68
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_sunsoft_dcs_device::write_h)
+{
+ LOG_MMC(("Sunsoft DCS write_h, offset %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x7000)
+ {
+ case 0x7000:
+ prg16_89ab(data & 0x07);
+ m_exrom_enable = !BIT(data, 3);
+ m_wram_enable = BIT(data, 4);
+ break;
+ default:
+ sun4_write(space, offset, data, mem_mask);
+ break;
+ }
+}
+
+READ8_MEMBER(nes_sunsoft_dcs_device::read_h)
+{
+ LOG_MMC(("Sunsoft DCS read_h, offset: %04x\n", offset));
+
+ if (m_exrom_enable && m_subslot->m_cart && offset < 0x4000)
+ {
+ if (m_timer_on)
+ return m_subslot->m_cart->read(space, offset, mem_mask);
+ else
+ return m_open_bus; // after the timer is off, this returns open bus...
+ }
+ else
+ return hi_access_rom(offset);
+}
+
+WRITE8_MEMBER(nes_sunsoft_dcs_device::write_m)
+{
+ LOG_MMC(("Sunsoft DCS write_m, offset: %04x, data: %02x\n", offset, data));
+
+ if (!m_battery.empty() && m_wram_enable)
+ m_battery[offset & (m_battery.size() - 1)] = data;
+ if (!m_prgram.empty() && m_wram_enable)
+ m_prgram[offset & (m_prgram.size() - 1)] = data;
+ if (!m_wram_enable && !m_timer_on)
+ {
+ m_timer_on = 1;
+ // start NTB-ROM timer
+ ntb_enable_timer->adjust(timer_freq);
+ }
+}
+
+READ8_MEMBER(nes_sunsoft_dcs_device::read_m)
+{
+ LOG_MMC(("Sunsoft DCS read_m, offset: %04x\n", offset));
+
+ if (!m_battery.empty() && m_wram_enable)
+ return m_battery[offset & (m_battery.size() - 1)];
+ if (!m_prgram.empty() && m_wram_enable)
+ return m_prgram[offset & (m_prgram.size() - 1)];
+
+ return m_open_bus; // open bus
+}
+
+//-------------------------------------------------
+// MACHINE_CONFIG_FRAGMENT( sub_slot )
+//-------------------------------------------------
+
+static SLOT_INTERFACE_START(ntb_cart)
+ SLOT_INTERFACE_INTERNAL("ntbrom", NES_NTB_ROM)
+SLOT_INTERFACE_END
+
+static MACHINE_CONFIG_FRAGMENT( sub_slot )
+ MCFG_NTB_MINICART_ADD("ntb_slot", ntb_cart)
+MACHINE_CONFIG_END
+
+
+//-------------------------------------------------
+// machine_config_additions - device-specific
+// machine configurations
+//-------------------------------------------------
+
+machine_config_constructor nes_sunsoft_dcs_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( sub_slot );
+}
+
+
+//-------------------------------------------------
+// device_timer - handler timer events
+//-------------------------------------------------
+
+void nes_sunsoft_dcs_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if (id == TIMER_PROTECT)
+ {
+ m_timer_on = 0;
+ ntb_enable_timer->reset();
+ }
+}
diff --git a/src/devices/bus/nes/sunsoft_dcs.h b/src/devices/bus/nes/sunsoft_dcs.h
new file mode 100644
index 00000000000..67fcaf83b1e
--- /dev/null
+++ b/src/devices/bus/nes/sunsoft_dcs.h
@@ -0,0 +1,150 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_SUNSOFT_DCS_H
+#define __NES_SUNSOFT_DCS_H
+
+#include "sunsoft.h"
+
+
+//-----------------------------------------------
+//
+// Nantettate!! Baseball Cartslot implementation
+//
+//-----------------------------------------------
+
+// ======================> ntb_cart_interface
+
+class ntb_cart_interface : public device_slot_card_interface
+{
+public:
+ // construction/destruction
+ ntb_cart_interface(const machine_config &mconfig, device_t &device);
+ virtual ~ntb_cart_interface();
+
+ // reading and writing
+ virtual DECLARE_READ8_MEMBER(read) { return m_rom[offset]; }
+
+ UINT8 *get_cart_base() { return m_rom; }
+
+protected:
+ // internal state
+ UINT8 *m_rom;
+};
+
+// ======================> nes_ntb_slot_device
+
+class nes_ntb_slot_device : public device_t,
+ public device_image_interface,
+ public device_slot_interface
+{
+public:
+ // construction/destruction
+ nes_ntb_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+ virtual ~nes_ntb_slot_device();
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_config_complete() { update_names(); }
+
+ // image-level overrides
+ virtual bool call_load();
+ virtual bool call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry);
+
+ virtual iodevice_t image_type() const { return IO_CARTSLOT; }
+ virtual bool is_readable() const { return 1; }
+ virtual bool is_writeable() const { return 0; }
+ virtual bool is_creatable() const { return 0; }
+ virtual bool must_be_loaded() const { return 0; }
+ virtual bool is_reset_on_load() const { return 1; }
+ virtual const char *image_interface() const { return "ntb_cart"; }
+ virtual const char *file_extensions() const { return "bin"; }
+ virtual const option_guide *create_option_guide() const { return NULL; }
+
+ // slot interface overrides
+ virtual void get_default_card_software(std::string &result);
+
+ virtual DECLARE_READ8_MEMBER(read);
+
+ ntb_cart_interface* m_cart;
+};
+
+// device type definition
+extern const device_type NES_NTB_SLOT;
+
+
+#define MCFG_NTB_MINICART_ADD(_tag, _slot_intf) \
+ MCFG_DEVICE_ADD(_tag, NES_NTB_SLOT, 0) \
+ MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, NULL, false)
+
+
+//-----------------------------------------------
+//
+// Nantettate!! Baseball Minicart implementation
+//
+//-----------------------------------------------
+
+// ======================> nes_ntb_rom_device
+
+class nes_ntb_rom_device : public device_t,
+ public ntb_cart_interface
+{
+public:
+ // construction/destruction
+ nes_ntb_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // optional information overrides
+ virtual const rom_entry *device_rom_region() const;
+ virtual UINT8* get_cart_base();
+
+protected:
+ // device-level overrides
+ virtual void device_start();
+};
+
+// device type definition
+extern const device_type NES_NTB_ROM;
+
+
+
+//------------------------------------------------
+//
+// Nantettate!! Baseball base cart implementation
+// a.k.a. Sunsoft Dual Cassette System
+// (variant of Sunsoft-4 PCB)
+//
+//------------------------------------------------
+
+// ======================> nes_sunsoft_dcs_device
+
+class nes_sunsoft_dcs_device : public nes_sunsoft_4_device
+{
+public:
+ // construction/destruction
+ nes_sunsoft_dcs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual machine_config_constructor device_mconfig_additions() const;
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_READ8_MEMBER(read_h);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+private:
+ int m_timer_on, m_exrom_enable;
+ required_device<nes_ntb_slot_device> m_subslot;
+
+ static const device_timer_id TIMER_PROTECT = 0;
+ emu_timer *ntb_enable_timer;
+ attotime timer_freq;
+};
+
+
+
+// device type definition
+extern const device_type NES_SUNSOFT_DCS;
+
+#endif
diff --git a/src/devices/bus/nes/taito.c b/src/devices/bus/nes/taito.c
new file mode 100644
index 00000000000..2338d91ee25
--- /dev/null
+++ b/src/devices/bus/nes/taito.c
@@ -0,0 +1,470 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for Taito PCBs
+
+
+ Here we emulate the following PCBs
+
+ * Taito TC0190FMC/TC0190FMR [mapper 33]
+ * Taito TC0190FMC+PAL16R4 [mapper 48]
+ * Taito X1-005 [mapper 80, 207]
+ * Taito X1-017 [mapper 82]
+
+ TODO:
+ - Akira does not work. Investigate why!
+ - Don Doko Don 2 freezes when you get to the first boss
+
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "taito.h"
+
+#include "cpu/m6502/m6502.h"
+#include "video/ppu2c0x.h" // this has to be included so that IRQ functions can access PPU_BOTTOM_VISIBLE_SCANLINE
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_TC0190FMC = &device_creator<nes_tc0190fmc_device>;
+const device_type NES_TC0190FMC_PAL16R4 = &device_creator<nes_tc0190fmc_pal16r4_device>;
+const device_type NES_X1_005 = &device_creator<nes_x1_005_device>;
+const device_type NES_X1_017 = &device_creator<nes_x1_017_device>;
+
+
+nes_tc0190fmc_device::nes_tc0190fmc_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : nes_nrom_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+nes_tc0190fmc_device::nes_tc0190fmc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_TC0190FMC, "NES Cart Taito TC0190FMC PCB", tag, owner, clock, "nes_tc0190fmc", __FILE__)
+{
+}
+
+nes_tc0190fmc_pal16r4_device::nes_tc0190fmc_pal16r4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_tc0190fmc_device(mconfig, NES_TC0190FMC_PAL16R4, "NES Cart Taito TC0190FMC + PAL16R4 PCB", tag, owner, clock, "nes_tc0190pal", __FILE__)
+{
+}
+
+nes_x1_005_device::nes_x1_005_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_X1_005, "NES Cart Taito X1-005 PCB", tag, owner, clock, "nes_x1_005", __FILE__)
+{
+}
+
+nes_x1_017_device::nes_x1_017_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_X1_017, "NES Cart Taito X1-017 PCB", tag, owner, clock, "nes_x1_017", __FILE__)
+{
+}
+
+
+
+void nes_tc0190fmc_device::device_start()
+{
+ common_start();
+}
+
+void nes_tc0190fmc_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+}
+
+void nes_tc0190fmc_pal16r4_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_irq_count_latch));
+}
+
+void nes_tc0190fmc_pal16r4_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_irq_enable = 0;
+ m_irq_count = 0;
+ m_irq_count_latch = 0;
+}
+
+void nes_x1_005_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+
+ save_item(NAME(m_x1_005_ram));
+
+ m_mapper_sram_size = 0x80;
+ m_mapper_sram = m_x1_005_ram;
+}
+
+void nes_x1_005_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_latch = 0;
+}
+
+void nes_x1_017_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+ save_item(NAME(m_reg));
+ save_item(NAME(m_mmc_vrom_bank));
+
+ save_item(NAME(m_x1_017_ram));
+
+ m_mapper_sram_size = 0x1400;
+ m_mapper_sram = m_x1_017_ram;
+}
+
+void nes_x1_017_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_latch = 0;
+ memset(m_reg, 0, sizeof(m_reg));
+ memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank));
+}
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ Taito TC0190FMC board emulation
+
+ Games: Akira, Bakushou!! Jinsei Gekijou, Don Doko Don,
+ Insector X, Operation Wolf, Power Blazer, Takeshi no
+ Sengoku Fuuunji
+
+ iNES: mapper 33
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_tc0190fmc_device::tc0190fmc_write)
+{
+ LOG_MMC(("tc0190fmc_write, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x7003)
+ {
+ case 0x0000:
+ set_nt_mirroring(BIT(data, 6) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ prg8_89(data);
+ break;
+ case 0x0001:
+ prg8_ab(data);
+ break;
+ case 0x0002:
+ chr2_0(data, CHRROM);
+ break;
+ case 0x0003:
+ chr2_2(data, CHRROM);
+ break;
+ case 0x2000:
+ chr1_4(data, CHRROM);
+ break;
+ case 0x2001:
+ chr1_5(data, CHRROM);
+ break;
+ case 0x2002:
+ chr1_6(data, CHRROM);
+ break;
+ case 0x2003:
+ chr1_7(data, CHRROM);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Taito TC0190FMC + PAL16R4 board emulation
+
+ Games: Bakushou!! Jinsei Gekijou 3, Bubble Bobble 2,
+ Captain Saver, Don Doko Don 2, Flintstones, Jetsons
+
+ This is basically Mapper 33 + IRQ. Notably, IRQ works the
+ same as MMC3 irq, BUT latch values are "inverted" (XOR'ed
+ with 0xff) and there is a little delay (not implemented yet)
+ We simply use MMC3 IRQ and XOR the value written in the
+ register 0xc000 below
+
+ iNES: mapper 48
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+void nes_tc0190fmc_pal16r4_device::hblank_irq( int scanline, int vblank, int blanked )
+{
+ if (scanline < PPU_BOTTOM_VISIBLE_SCANLINE)
+ {
+ int prior_count = m_irq_count;
+ if (m_irq_count == 0)
+ m_irq_count = m_irq_count_latch;
+ else
+ m_irq_count--;
+
+ if (m_irq_enable && !blanked && (m_irq_count == 0) && prior_count)
+ m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
+ }
+}
+
+WRITE8_MEMBER(nes_tc0190fmc_pal16r4_device::write_h)
+{
+ LOG_MMC(("tc0190fmc pal16r4 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x7003)
+ {
+ case 0x0000:
+ prg8_89(data);
+ break;
+ case 0x0001:
+ case 0x0002:
+ case 0x0003:
+ case 0x2000:
+ case 0x2001:
+ case 0x2002:
+ case 0x2003:
+ tc0190fmc_write(space, offset, data, mem_mask);
+ break;
+ case 0x4000:
+ m_irq_count_latch = (0x100 - data) & 0xff;
+ break;
+ case 0x4001:
+ m_irq_count = m_irq_count_latch;
+ break;
+ case 0x4002:
+ m_irq_enable = 1;
+ break;
+ case 0x4003:
+ m_irq_enable = 0;
+ m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
+ break;
+ case 0x6000:
+ set_nt_mirroring(BIT(data, 6) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Taito X1-005 board emulation
+
+ Actually, Fudou Myouou Den uses a variant of the board with
+ CIRAM, making necessary two distinct mappers & pcb_id.
+
+ Also, we miss to emulate the security check at 0x7ef8 / 0x7ef9
+ and the 0x80 ram!
+
+ iNES: mappers 80 & 207
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_x1_005_device::write_m)
+{
+ LOG_MMC(("x1_005 write_m, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset)
+ {
+ case 0x1ef0:
+ chr2_0((data & 0x7f) >> 1, CHRROM);
+ if (m_x1_005_alt_mirroring)
+ {
+ set_nt_page(0, CIRAM, (data & 0x80) ? 1 : 0, 1);
+ set_nt_page(1, CIRAM, (data & 0x80) ? 1 : 0, 1);
+ }
+ break;
+ case 0x1ef1:
+ chr2_2((data & 0x7f) >> 1, CHRROM);
+ if (m_x1_005_alt_mirroring)
+ {
+ set_nt_page(2, CIRAM, (data & 0x80) ? 1 : 0, 1);
+ set_nt_page(3, CIRAM, (data & 0x80) ? 1 : 0, 1);
+ }
+ break;
+ case 0x1ef2:
+ chr1_4(data, CHRROM);
+ break;
+ case 0x1ef3:
+ chr1_5(data, CHRROM);
+ break;
+ case 0x1ef4:
+ chr1_6(data, CHRROM);
+ break;
+ case 0x1ef5:
+ chr1_7(data, CHRROM);
+ break;
+ case 0x1ef6:
+ case 0x1ef7:
+ if (!m_x1_005_alt_mirroring)
+ set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ);
+ break;
+ case 0x1ef8:
+ case 0x1ef9:
+ m_latch = data;
+ break;
+ case 0x1efa:
+ case 0x1efb:
+ prg8_89(data);
+ break;
+ case 0x1efc:
+ case 0x1efd:
+ prg8_ab(data);
+ break;
+ case 0x1efe:
+ case 0x1eff:
+ prg8_cd(data);
+ break;
+ default:
+ logerror("mapper80_m_w uncaught addr: %04x, value: %02x\n", offset + 0x6000, data);
+ break;
+ }
+
+ if (offset >= 0x1f00 && m_latch == 0xa3)
+ m_x1_005_ram[offset & 0x7f] = data;
+}
+
+READ8_MEMBER(nes_x1_005_device::read_m)
+{
+ LOG_MMC(("x1_005 read_m, offset: %04x\n", offset));
+
+ if (offset >= 0x1f00 && m_latch == 0xa3)
+ return m_x1_005_ram[offset & 0x7f];
+
+ return m_open_bus; // open bus
+}
+
+/*-------------------------------------------------
+
+ Taito X1-017 board emulation
+
+ We miss to emulate the security check at 0x6000-0x73ff
+ and the ram!
+
+ Games: Kyuukyoku Harikiri Koushien, Kyuukyoku Harikiri
+ Stadium, SD Keiji - Blader
+
+ iNES: mapper 82
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+void nes_x1_017_device::set_chr()
+{
+ if (m_latch)
+ {
+ chr2_4(m_mmc_vrom_bank[0] >> 1, CHRROM);
+ chr2_6(m_mmc_vrom_bank[1] >> 1, CHRROM);
+ }
+ else
+ {
+ chr2_0(m_mmc_vrom_bank[0] >> 1, CHRROM);
+ chr2_2(m_mmc_vrom_bank[1] >> 1, CHRROM);
+ }
+ chr1_x(4 ^ m_latch, m_mmc_vrom_bank[2], CHRROM);
+ chr1_x(5 ^ m_latch, m_mmc_vrom_bank[3], CHRROM);
+ chr1_x(6 ^ m_latch, m_mmc_vrom_bank[4], CHRROM);
+ chr1_x(7 ^ m_latch, m_mmc_vrom_bank[5], CHRROM);
+}
+
+WRITE8_MEMBER(nes_x1_017_device::write_m)
+{
+ LOG_MMC(("x1017 write_m, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset)
+ {
+ case 0x1ef0:
+ case 0x1ef1:
+ case 0x1ef2:
+ case 0x1ef3:
+ case 0x1ef4:
+ case 0x1ef5:
+ if (m_mmc_vrom_bank[offset & 0x07] != data)
+ {
+ m_mmc_vrom_bank[offset & 0x07] = data;
+ set_chr();
+ }
+ break;
+ case 0x1ef6:
+ set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ);
+ if (m_latch != ((data & 0x02) << 1))
+ {
+ m_latch = ((data & 0x02) << 1);
+ set_chr();
+ }
+ break;
+ case 0x1ef7:
+ case 0x1ef8:
+ case 0x1ef9:
+ m_reg[(offset & 0x0f) - 7] = data;
+ break;
+ case 0x1efa:
+ prg8_89(data >> 2);
+ break;
+ case 0x1efb:
+ prg8_ab(data >> 2);
+ break;
+ case 0x1efc:
+ prg8_cd(data >> 2);
+ break;
+ default:
+ logerror("x1017_m_w uncaught write, addr: %04x, value: %02x\n", offset + 0x6000, data);
+ break;
+ }
+
+ // 2+2+1 KB of Internal RAM can be independently enabled/disabled!
+ if (offset < 0x0800 && m_reg[0] == 0xca)
+ m_x1_017_ram[0x0000 + (offset & 0x7ff)] = data;
+ if (offset < 0x1000 && m_reg[1] == 0x69)
+ m_x1_017_ram[0x0800 + (offset & 0x7ff)] = data;
+ if (offset < 0x1400 && m_reg[2] == 0x84)
+ m_x1_017_ram[0x1000 + (offset & 0x3ff)] = data;
+}
+
+READ8_MEMBER(nes_x1_017_device::read_m)
+{
+ LOG_MMC(("x1017 read_m, offset: %04x\n", offset));
+
+ // 2+2+1 KB of Internal RAM can be independently enabled/disabled!
+ if (offset < 0x0800 && m_reg[0] == 0xca)
+ return m_x1_017_ram[0x0000 + (offset & 0x7ff)];
+ if (offset < 0x1000 && m_reg[1] == 0x69)
+ return m_x1_017_ram[0x0800 + (offset & 0x7ff)];
+ if (offset < 0x1400 && m_reg[2] == 0x84)
+ return m_x1_017_ram[0x1000 + (offset & 0x3ff)];
+
+ return m_open_bus; // open bus
+}
diff --git a/src/devices/bus/nes/taito.h b/src/devices/bus/nes/taito.h
new file mode 100644
index 00000000000..9f2b57ca512
--- /dev/null
+++ b/src/devices/bus/nes/taito.h
@@ -0,0 +1,104 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_TAITO_H
+#define __NES_TAITO_H
+
+#include "nxrom.h"
+
+
+// ======================> nes_tc0190fmc_device
+
+class nes_tc0190fmc_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_tc0190fmc_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_tc0190fmc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(tc0190fmc_write);
+ virtual DECLARE_WRITE8_MEMBER(write_h) { tc0190fmc_write(space, offset, data, mem_mask); }
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_tc0190fmc_pal16r4_device
+
+class nes_tc0190fmc_pal16r4_device : public nes_tc0190fmc_device
+{
+public:
+ // construction/destruction
+ nes_tc0190fmc_pal16r4_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void hblank_irq(int scanline, int vblank, int blanked);
+ virtual void pcb_reset();
+
+private:
+ UINT16 m_irq_count, m_irq_count_latch;
+ int m_irq_enable;
+};
+
+
+// ======================> nes_x1_005_device
+
+class nes_x1_005_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_x1_005_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+
+ virtual void pcb_reset();
+
+private:
+ UINT8 m_latch;
+ // Taito X1-005 chip contains 80 bytes of internal ram, possibly battery backed up
+ UINT8 m_x1_005_ram[0x80];
+};
+
+
+// ======================> nes_x1_017_device
+
+class nes_x1_017_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_x1_017_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_m);
+ virtual DECLARE_WRITE8_MEMBER(write_m);
+
+ virtual void pcb_reset();
+
+private:
+ void set_chr();
+ UINT8 m_latch;
+ UINT8 m_reg[3]; //mapper ram protect
+ UINT8 m_mmc_vrom_bank[6];
+ // Taito X1-017 chip contains 5K of internal ram, battery backed up
+ UINT8 m_x1_017_ram[0x1400];
+};
+
+
+
+
+// device type definition
+extern const device_type NES_TC0190FMC;
+extern const device_type NES_TC0190FMC_PAL16R4;
+extern const device_type NES_X1_005;
+extern const device_type NES_X1_017;
+
+
+#endif
diff --git a/src/devices/bus/nes/tengen.c b/src/devices/bus/nes/tengen.c
new file mode 100644
index 00000000000..555b0da3f35
--- /dev/null
+++ b/src/devices/bus/nes/tengen.c
@@ -0,0 +1,376 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for Tengen PCBs
+
+
+ Here we emulate the following PCBs
+
+ * Tengen 800008
+ * Tengen 800032 [mapper 64]
+ * Tengen 800037 [mapper 158]
+
+ TODO:
+ - emulated the IRQ delay in 800032 (possibly reason of Skull & Crossbones not working?)
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "tengen.h"
+
+#include "cpu/m6502/m6502.h"
+#include "video/ppu2c0x.h" // this has to be included so that IRQ functions can access PPU_BOTTOM_VISIBLE_SCANLINE
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_TENGEN_800008 = &device_creator<nes_tengen008_device>;
+const device_type NES_TENGEN_800032 = &device_creator<nes_tengen032_device>;
+const device_type NES_TENGEN_800037 = &device_creator<nes_tengen037_device>;
+
+
+nes_tengen008_device::nes_tengen008_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_TENGEN_800008, "NES Cart Tengen 800008 PCB", tag, owner, clock, "nes_tengen008", __FILE__)
+{
+}
+
+nes_tengen032_device::nes_tengen032_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : nes_nrom_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+nes_tengen032_device::nes_tengen032_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_TENGEN_800032, "NES Cart Tengen 800032 PCB", tag, owner, clock, "nes_tengen032", __FILE__)
+{
+}
+
+nes_tengen037_device::nes_tengen037_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_tengen032_device(mconfig, NES_TENGEN_800037, "NES Cart Tengen 800037 PCB", tag, owner, clock, "nes_tengen037", __FILE__)
+{
+}
+
+
+
+
+void nes_tengen008_device::device_start()
+{
+ common_start();
+}
+
+void nes_tengen008_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_tengen032_device::device_start()
+{
+ common_start();
+ irq_timer = timer_alloc(TIMER_IRQ);
+ irq_timer->reset();
+ timer_freq = machine().device<cpu_device>("maincpu")->cycles_to_attotime(4);
+
+ save_item(NAME(m_mmc_prg_bank));
+ save_item(NAME(m_mmc_vrom_bank));
+ save_item(NAME(m_latch));
+
+ save_item(NAME(m_irq_mode));
+ save_item(NAME(m_irq_reset));
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_irq_count_latch));
+}
+
+void nes_tengen032_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(m_prg_chunks - 1);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+ memset(m_mmc_prg_bank, 0, sizeof(m_mmc_prg_bank));
+ memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank));
+
+ m_latch = 0;
+ m_irq_mode = 0;
+ m_irq_reset = 0;
+ m_irq_enable = 0;
+ m_irq_count = m_irq_count_latch = 0;
+}
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ Tengen 800008 Board
+
+ iNES: mapper 3?
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_tengen008_device::write_h)
+{
+ LOG_MMC(("tengen008 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ // this pcb is subject to bus conflict
+ data = account_bus_conflict(offset, data);
+
+ prg32(data >> 3);
+ chr8(data, CHRROM);
+}
+
+/*-------------------------------------------------
+
+ Tengen 800032 Board
+
+ Games: Klax, Road Runner, Rolling Thunder, Shinobi, Skulls
+ & Crossbones, Xybots
+
+ This is very similar to MMC-3 (or more probably to the
+ Namcot predecessor of MMC-3), but with more registers
+ and with an alternative IRQ mode
+
+ iNES: mapper 64
+
+ In MESS: Partially Supported (there should be a small
+ delay between the IRQ and its execution, but that is not
+ emulated yet: this is possibly the problem with Skulls
+ & Crossbones)
+
+ -------------------------------------------------*/
+
+inline void nes_tengen032_device::irq_clock(int blanked)
+{
+ // From NESdev wiki: Regardless of the mode used to clock the counter, every time the counter is clocked,
+ // the following actions occur:
+ // - If Reset reg ($C001) was written to after previous clock, reload IRQ counter with IRQ Reload + 1
+ // - Otherwise, if IRQ Counter is 0, reload IRQ counter with IRQ Reload value
+ // - Otherwise, first decrement IRQ counter by 1, then if IRQ counter is now 0 and IRQs are enabled,
+ // trigger IRQ
+ if (m_irq_reset)
+ {
+ m_irq_reset = 0;
+ m_irq_count = m_irq_count_latch + 1;
+ }
+ else if (!m_irq_count)
+ m_irq_count = m_irq_count_latch;
+
+ m_irq_count--;
+ if (m_irq_enable && !blanked && !m_irq_count)
+ m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
+}
+
+// we use the HBLANK IRQ latch from PPU for the scanline based IRQ mode
+// and a timer for the cycle based IRQ mode, which both call irq_clock
+
+void nes_tengen032_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ if (id == TIMER_IRQ)
+ {
+ irq_clock(0);
+ }
+}
+
+
+void nes_tengen032_device::hblank_irq(int scanline, int vblank, int blanked)
+{
+ if (!m_irq_mode) // we are in scanline mode!
+ {
+ if (scanline < PPU_BOTTOM_VISIBLE_SCANLINE)
+ {
+ irq_clock(blanked);
+ }
+ }
+}
+
+void nes_tengen032_device::set_prg()
+{
+ UINT8 prg_mode = m_latch & 0x40;
+
+ prg8_89(m_mmc_prg_bank[prg_mode ? 2: 0]);
+ prg8_ab(m_mmc_prg_bank[prg_mode ? 0: 1]);
+ prg8_cd(m_mmc_prg_bank[prg_mode ? 1: 2]);
+}
+
+void nes_tengen032_device::chr_cb(int start, int bank, int source)
+{
+ chr1_x(start, bank, source);
+}
+
+void nes_tengen032_device::set_chr()
+{
+ UINT8 chr_page = (m_latch & 0x80) >> 5;
+
+ if (m_latch & 0x20)
+ {
+ chr_cb(0 ^ chr_page, m_mmc_vrom_bank[0], CHRROM);
+ chr_cb(1 ^ chr_page, m_mmc_vrom_bank[6], CHRROM);
+ chr_cb(2 ^ chr_page, m_mmc_vrom_bank[1], CHRROM);
+ chr_cb(3 ^ chr_page, m_mmc_vrom_bank[7], CHRROM);
+ }
+ else
+ {
+ chr_cb(0 ^ chr_page, m_mmc_vrom_bank[0] & ~0x01, CHRROM);
+ chr_cb(1 ^ chr_page, m_mmc_vrom_bank[0] | 0x01, CHRROM);
+ chr_cb(2 ^ chr_page, m_mmc_vrom_bank[1] & ~0x01, CHRROM);
+ chr_cb(3 ^ chr_page, m_mmc_vrom_bank[1] | 0x01, CHRROM);
+ }
+
+ chr_cb(4 ^ chr_page, m_mmc_vrom_bank[2], CHRROM);
+ chr_cb(5 ^ chr_page, m_mmc_vrom_bank[3], CHRROM);
+ chr_cb(6 ^ chr_page, m_mmc_vrom_bank[4], CHRROM);
+ chr_cb(7 ^ chr_page, m_mmc_vrom_bank[5], CHRROM);
+}
+
+WRITE8_MEMBER(nes_tengen032_device::tengen032_write)
+{
+ UINT8 helper, cmd;
+ LOG_MMC(("tengen032_write, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x6001)
+ {
+ case 0x0000:
+ helper = m_latch ^ data;
+ m_latch = data;
+
+ // Has PRG Mode changed?
+ if (helper & 0x40)
+ set_prg();
+
+ // Has CHR Mode changed?
+ if (helper & 0xa0)
+ set_chr();
+ break;
+
+ case 0x0001:
+ cmd = m_latch & 0x0f;
+ switch (cmd)
+ {
+ case 0: case 1:
+ case 2: case 3:
+ case 4: case 5:
+ m_mmc_vrom_bank[cmd] = data;
+ set_chr();
+ break;
+ case 6: case 7:
+ m_mmc_prg_bank[cmd - 6] = data;
+ set_prg();
+ break;
+ case 8: case 9:
+ m_mmc_vrom_bank[cmd - 2] = data;
+ set_chr();
+ break;
+ case 0x0f:
+ m_mmc_prg_bank[2] = data;
+ set_prg();
+ break;
+ }
+ break;
+
+ case 0x2000:
+ set_nt_mirroring(BIT(data, 0) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ break;
+
+ case 0x4000:
+ m_irq_count_latch = data;
+ break;
+
+ case 0x4001: /* $c001 - IRQ scanline latch */
+ m_irq_mode = data & 0x01;
+ if (m_irq_mode)
+ irq_timer->adjust(attotime::zero, 0, timer_freq);
+ else
+ irq_timer->adjust(attotime::never);
+ m_irq_reset = 1;
+ break;
+
+ case 0x6000:
+ m_irq_enable = 0;
+ m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
+ break;
+
+ case 0x6001:
+ m_irq_enable = 1;
+ break;
+
+ default:
+ LOG_MMC(("Tengen 800032 write. addr: %04x value: %02x\n", offset + 0x8000, data));
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Tengen 800037 Board
+
+ Games: Alien Syndrome
+
+ Same as above (mapper chip RAMBO-1) but CHR A17 output
+ is connected to CIRAM A10, so this differs from 800032
+ exactly in the same way as TLSROM differs from plain
+ MMC-3
+
+ iNES: mapper 158
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+void nes_tengen037_device::set_mirror()
+{
+ if (m_latch & 0x80)
+ {
+ set_nt_page(0, CIRAM, BIT(m_mmc_vrom_bank[2],7), 1);
+ set_nt_page(1, CIRAM, BIT(m_mmc_vrom_bank[3],7), 1);
+ set_nt_page(2, CIRAM, BIT(m_mmc_vrom_bank[4],7), 1);
+ set_nt_page(3, CIRAM, BIT(m_mmc_vrom_bank[5],7), 1);
+ }
+ else
+ {
+ set_nt_page(0, CIRAM, BIT(m_mmc_vrom_bank[0],7), 1);
+ set_nt_page(1, CIRAM, BIT(m_mmc_vrom_bank[0],7), 1);
+ set_nt_page(2, CIRAM, BIT(m_mmc_vrom_bank[1],7), 1);
+ set_nt_page(3, CIRAM, BIT(m_mmc_vrom_bank[1],7), 1);
+ }
+}
+
+void nes_tengen037_device::chr_cb( int start, int bank, int source )
+{
+ set_mirror(); // we could probably update only for one (e.g. the first) call, to slightly optimize the code
+ chr1_x(start, bank, source);
+}
+
+
+WRITE8_MEMBER(nes_tengen037_device::write_h)
+{
+ LOG_MMC(("tengen037 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x6001)
+ {
+ case 0x2000:
+ break;
+
+ default:
+ tengen032_write(space, offset, data, mem_mask);
+ break;
+ }
+}
diff --git a/src/devices/bus/nes/tengen.h b/src/devices/bus/nes/tengen.h
new file mode 100644
index 00000000000..8732e6e1321
--- /dev/null
+++ b/src/devices/bus/nes/tengen.h
@@ -0,0 +1,88 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_TENGEN_H
+#define __NES_TENGEN_H
+
+#include "nxrom.h"
+
+
+// ======================> nes_tengen008_device
+
+class nes_tengen008_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_tengen008_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_tengen032_device
+
+class nes_tengen032_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_tengen032_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_tengen032_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+ virtual DECLARE_WRITE8_MEMBER(tengen032_write);
+ virtual DECLARE_WRITE8_MEMBER(write_h) { tengen032_write(space, offset, data, mem_mask); }
+ virtual void chr_cb(int start, int bank, int source);
+
+ virtual void hblank_irq(int scanline, int vblank, int blanked);
+ virtual void pcb_reset();
+
+protected:
+ void set_prg();
+ void set_chr();
+ void irq_clock(int blanked);
+
+ UINT16 m_irq_count, m_irq_count_latch;
+ UINT8 m_irq_mode, m_irq_reset;
+ int m_irq_enable;
+
+ UINT8 m_latch;
+ UINT8 m_mmc_prg_bank[3];
+ UINT8 m_mmc_vrom_bank[8];
+
+ static const device_timer_id TIMER_IRQ = 0;
+ emu_timer *irq_timer;
+ attotime timer_freq;
+};
+
+
+// ======================> nes_tengen037_device
+
+class nes_tengen037_device : public nes_tengen032_device
+{
+public:
+ // construction/destruction
+ nes_tengen037_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+ virtual void chr_cb(int start, int bank, int source);
+
+protected:
+ void set_mirror();
+};
+
+
+
+
+
+// device type definition
+extern const device_type NES_TENGEN_800008;
+extern const device_type NES_TENGEN_800032;
+extern const device_type NES_TENGEN_800037;
+
+#endif
diff --git a/src/devices/bus/nes/txc.c b/src/devices/bus/nes/txc.c
new file mode 100644
index 00000000000..ae36ece75ee
--- /dev/null
+++ b/src/devices/bus/nes/txc.c
@@ -0,0 +1,268 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for TXC PCBs
+
+ Here we emulate the following PCBs
+
+ * TXC 22111 [mapper 132]
+ * TXC Du Ma Racing [mapper 172]
+ * TXC Mahjong Block [mapper 172]
+ * TXC Strike Wolf [mapper 36]
+ * TXC Commandos (and many more) [mapper 241]
+
+ TODO:
+ - Does Commandos indeed use this board?
+
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "txc.h"
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_TXC_22211 = &device_creator<nes_txc_22211_device>;
+const device_type NES_TXC_DUMARACING = &device_creator<nes_txc_dumarc_device>;
+const device_type NES_TXC_MJBLOCK = &device_creator<nes_txc_mjblock_device>;
+const device_type NES_TXC_STRIKEW = &device_creator<nes_txc_strikew_device>;
+const device_type NES_TXC_COMMANDOS = &device_creator<nes_txc_commandos_device>;
+
+
+nes_txc_22211_device::nes_txc_22211_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : nes_nrom_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+nes_txc_22211_device::nes_txc_22211_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_TXC_22211, "NES Cart TXC 22211 PCB", tag, owner, clock, "nes_txc_22211", __FILE__)
+{
+}
+
+nes_txc_dumarc_device::nes_txc_dumarc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_txc_22211_device(mconfig, NES_TXC_DUMARACING, "NES Cart TXC Du Ma Racing PCB", tag, owner, clock, "nes_dumarc", __FILE__)
+{
+}
+
+nes_txc_mjblock_device::nes_txc_mjblock_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_txc_22211_device(mconfig, NES_TXC_MJBLOCK, "NES Cart TXC Mahjong Block PCB", tag, owner, clock, "nes_mjblock", __FILE__)
+{
+}
+
+nes_txc_strikew_device::nes_txc_strikew_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_TXC_STRIKEW, "NES Cart Strike Wolf PCB", tag, owner, clock, "nes_txc_strikew", __FILE__)
+{
+}
+
+nes_txc_commandos_device::nes_txc_commandos_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_TXC_COMMANDOS, "NES Cart Commandos (and others) PCB", tag, owner, clock, "nes_txc_comm", __FILE__)
+{
+}
+
+
+
+
+void nes_txc_22211_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_reg));
+}
+
+void nes_txc_22211_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+ m_reg[0] = m_reg[1] = m_reg[2] = m_reg[3] = 0;
+}
+
+void nes_txc_strikew_device::device_start()
+{
+ common_start();
+}
+
+void nes_txc_strikew_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_txc_commandos_device::device_start()
+{
+ common_start();
+}
+
+void nes_txc_commandos_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ Bootleg Board 22211 by TXC (Type A)
+
+ Games: Creatom
+
+ Info from NEStopia: this mapper features write to four
+ registers (0x4100-0x4103). The third one is used to select
+ PRG and CHR banks.
+
+ iNES: mapper 132
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_txc_22211_device::write_l)
+{
+ LOG_MMC(("TXC 22111 write_l, offset: %04x, data: %02x\n", offset, data));
+
+ if (offset < 4)
+ m_reg[offset & 0x03] = data;
+}
+
+READ8_MEMBER(nes_txc_22211_device::read_l)
+{
+ LOG_MMC(("TXC 22111 read_l, offset: %04x\n", offset));
+
+ if (offset == 0x0000)
+ return (m_reg[1] ^ m_reg[2]) | 0x40;
+ else
+ return 0x00;
+}
+
+WRITE8_MEMBER(nes_txc_22211_device::write_h)
+{
+ LOG_MMC(("TXC 22111 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ prg32(m_reg[2] >> 2);
+ chr8(m_reg[2], CHRROM);
+}
+
+/*-------------------------------------------------
+
+ Bootleg Board 22211 by TXC (Type B)
+
+ Games: 1991 Du Ma Racing
+
+ This mapper is basically the same as Type A. Only difference is
+ in the way CHR banks are selected (see below)
+
+ iNES: mapper 172
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_txc_dumarc_device::write_h)
+{
+ LOG_MMC(("TXC Du Ma Racing write_h, offset: %04x, data: %02x\n", offset, data));
+
+ prg32(m_reg[2] >> 2);
+ chr8((((data ^ m_reg[2]) >> 3) & 0x02) | (((data ^ m_reg[2]) >> 5) & 0x01), CHRROM);
+}
+
+/*-------------------------------------------------
+
+ Bootleg Board 22211 by TXC (Type C)
+
+ Games: Mahjong Block, Xiao Ma Li
+
+ This mapper is basically the same as 132 too. Only difference is
+ in 0x4100 reads which expect also bit 0 to be set
+
+ iNES: mapper 172
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+READ8_MEMBER(nes_txc_mjblock_device::read_l)
+{
+ LOG_MMC(("TXC mjblock read_l, offset: %04x\n", offset));
+
+ if (offset == 0x0000)
+ return (m_reg[1] ^ m_reg[2]) | 0x41;
+ else
+ return 0x00;
+}
+
+/*-------------------------------------------------
+
+ Bootleg Board 'Strike Wolf' by TXC
+
+ Games: Strike Wolf and Policeman
+
+ iNES: mapper 36
+
+ In MESS: Partially Supported (Policeman has glitches)
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_txc_strikew_device::write_h)
+{
+ LOG_MMC(("TXC Strike Wolf write_h, offset: %04x, data: %02x\n", offset, data));
+
+ // this pcb is subject to bus conflict
+ data = account_bus_conflict(offset, data);
+
+ if ((offset >= 0x400) && (offset < 0x7fff))
+ {
+ prg32(data >> 4);
+ chr8(data & 0x0f, CHRROM);
+ }
+}
+
+/*-------------------------------------------------
+
+ Bootleg Board MXMDHTWO by TXC
+
+ Games: Commandos, Journey to the West, Ma Bu Mi Zhen &
+ Qu Wei Cheng Yu Wu, Si Lu Chuan Qi
+
+ Simple Mapper: writes to 0x8000-0xffff sets the prg32 bank.
+ Not sure if returning 0x50 for reads in 0x4100-0x5000 is correct.
+
+ iNES: mapper 241
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+READ8_MEMBER(nes_txc_commandos_device::read_l)
+{
+ return 0x50;
+}
+
+WRITE8_MEMBER(nes_txc_commandos_device::write_h)
+{
+ LOG_MMC(("TXC Commandos write_h, offset: %04x, data: %02x\n", offset, data));
+
+ prg32(data);
+}
diff --git a/src/devices/bus/nes/txc.h b/src/devices/bus/nes/txc.h
new file mode 100644
index 00000000000..e71e6fde249
--- /dev/null
+++ b/src/devices/bus/nes/txc.h
@@ -0,0 +1,99 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_TXC_H
+#define __NES_TXC_H
+
+#include "nxrom.h"
+
+
+// ======================> nes_txc_22211_device
+
+class nes_txc_22211_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_txc_22211_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_txc_22211_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_l);
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+protected:
+ UINT8 m_reg[4];
+};
+
+
+// ======================> nes_txc_dumarac_device
+
+class nes_txc_dumarc_device : public nes_txc_22211_device
+{
+public:
+ // construction/destruction
+ nes_txc_dumarc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+};
+
+
+// ======================> nes_txc_mjblock_device
+
+class nes_txc_mjblock_device : public nes_txc_22211_device
+{
+public:
+ // construction/destruction
+ nes_txc_mjblock_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual DECLARE_READ8_MEMBER(read_l);
+};
+
+
+// ======================> nes_txc_strikew_device
+
+class nes_txc_strikew_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_txc_strikew_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_txc_commandos_device
+
+class nes_txc_commandos_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_txc_commandos_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_l);
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+
+
+// device type definition
+extern const device_type NES_TXC_22211;
+extern const device_type NES_TXC_DUMARACING;
+extern const device_type NES_TXC_MJBLOCK;
+extern const device_type NES_TXC_STRIKEW;
+extern const device_type NES_TXC_COMMANDOS;
+
+#endif
diff --git a/src/devices/bus/nes/waixing.c b/src/devices/bus/nes/waixing.c
new file mode 100644
index 00000000000..8a223a8e295
--- /dev/null
+++ b/src/devices/bus/nes/waixing.c
@@ -0,0 +1,1244 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+/***********************************************************************************************************
+
+
+ NES/Famicom cartridge emulation for Waixing PCBs
+
+ Here we emulate the various PCBs used by Waixing for its games
+
+ TODO:
+ - investigate the PPU issues causing some games not to have sprites (e.g. some games using mappers 15, 164,
+ 242, 249)
+
+ ***********************************************************************************************************/
+
+
+#include "emu.h"
+#include "waixing.h"
+
+#include "cpu/m6502/m6502.h"
+
+
+#ifdef NES_PCB_DEBUG
+#define VERBOSE 1
+#else
+#define VERBOSE 0
+#endif
+
+#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+//-------------------------------------------------
+// constructor
+//-------------------------------------------------
+
+const device_type NES_WAIXING_A = &device_creator<nes_waixing_a_device>;
+const device_type NES_WAIXING_A1 = &device_creator<nes_waixing_a1_device>;
+const device_type NES_WAIXING_B = &device_creator<nes_waixing_b_device>;
+const device_type NES_WAIXING_C = &device_creator<nes_waixing_c_device>;
+const device_type NES_WAIXING_D = &device_creator<nes_waixing_d_device>;
+const device_type NES_WAIXING_E = &device_creator<nes_waixing_e_device>;
+const device_type NES_WAIXING_F = &device_creator<nes_waixing_f_device>;
+const device_type NES_WAIXING_G = &device_creator<nes_waixing_g_device>;
+const device_type NES_WAIXING_H = &device_creator<nes_waixing_h_device>;
+const device_type NES_WAIXING_H1 = &device_creator<nes_waixing_h1_device>;
+const device_type NES_WAIXING_I = &device_creator<nes_waixing_i_device>;
+const device_type NES_WAIXING_J = &device_creator<nes_waixing_j_device>;
+const device_type NES_WAIXING_SH2 = &device_creator<nes_waixing_sh2_device>;
+const device_type NES_WAIXING_SEC = &device_creator<nes_waixing_sec_device>;
+const device_type NES_WAIXING_SGZ = &device_creator<nes_waixing_sgz_device>;
+const device_type NES_WAIXING_SGZLZ = &device_creator<nes_waixing_sgzlz_device>;
+const device_type NES_WAIXING_FFV = &device_creator<nes_waixing_ffv_device>;
+const device_type NES_WAIXING_WXZS = &device_creator<nes_waixing_wxzs_device>;
+const device_type NES_WAIXING_DQ8 = &device_creator<nes_waixing_dq8_device>;
+const device_type NES_WAIXING_WXZS2 = &device_creator<nes_waixing_wxzs2_device>;
+const device_type NES_WAIXING_FS304 = &device_creator<nes_waixing_fs304_device>;
+
+
+nes_waixing_a_device::nes_waixing_a_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : nes_txrom_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+nes_waixing_a_device::nes_waixing_a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_txrom_device(mconfig, NES_WAIXING_A, "NES Cart Waixing Type A PCB", tag, owner, clock, "nes_waixing_a", __FILE__)
+{
+}
+
+nes_waixing_a1_device::nes_waixing_a1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_waixing_a_device(mconfig, NES_WAIXING_A1, "NES Cart Waixing Type A (Alt) PCB", tag, owner, clock, "nes_waixing_a1", __FILE__)
+{
+}
+
+nes_waixing_b_device::nes_waixing_b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_waixing_a_device(mconfig, NES_WAIXING_B, "NES Cart Waixing Type B PCB", tag, owner, clock, "nes_waixing_b", __FILE__)
+{
+}
+
+nes_waixing_c_device::nes_waixing_c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_waixing_a_device(mconfig, NES_WAIXING_C, "NES Cart Waixing Type C PCB", tag, owner, clock, "nes_waixing_c", __FILE__)
+{
+}
+
+nes_waixing_d_device::nes_waixing_d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_waixing_a_device(mconfig, NES_WAIXING_D, "NES Cart Waixing Type D PCB", tag, owner, clock, "nes_waixing_d", __FILE__)
+{
+}
+
+nes_waixing_e_device::nes_waixing_e_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_waixing_a_device(mconfig, NES_WAIXING_E, "NES Cart Waixing Type E PCB", tag, owner, clock, "nes_waixing_e", __FILE__)
+{
+}
+
+nes_waixing_f_device::nes_waixing_f_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_waixing_a_device(mconfig, NES_WAIXING_F, "NES Cart Waixing Type F PCB", tag, owner, clock, "nes_waixing_f", __FILE__)
+{
+}
+
+nes_waixing_g_device::nes_waixing_g_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_waixing_a_device(mconfig, NES_WAIXING_G, "NES Cart Waixing Type G PCB", tag, owner, clock, "nes_waixing_g", __FILE__)
+{
+}
+
+nes_waixing_h_device::nes_waixing_h_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : nes_txrom_device(mconfig, type, name, tag, owner, clock, shortname, source)
+{
+}
+
+nes_waixing_h_device::nes_waixing_h_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_txrom_device(mconfig, NES_WAIXING_H, "NES Cart Waixing Type H PCB", tag, owner, clock, "nes_waixing_h", __FILE__)
+{
+}
+
+nes_waixing_h1_device::nes_waixing_h1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_waixing_h_device(mconfig, NES_WAIXING_H1, "NES Cart Waixing Type H (Alt) PCB", tag, owner, clock, "nes_waixing_h1", __FILE__)
+{
+}
+
+nes_waixing_i_device::nes_waixing_i_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_waixing_a_device(mconfig, NES_WAIXING_I, "NES Cart Waixing Type I PCB", tag, owner, clock, "nes_waixing_i", __FILE__)
+{
+}
+
+nes_waixing_j_device::nes_waixing_j_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_waixing_a_device(mconfig, NES_WAIXING_J, "NES Cart Waixing Type J PCB", tag, owner, clock, "nes_waixing_j", __FILE__)
+{
+}
+
+nes_waixing_sh2_device::nes_waixing_sh2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_txrom_device(mconfig, NES_WAIXING_SH2, "NES Cart Waixing SH2 PCB", tag, owner, clock, "nes_waixing_sh2", __FILE__)
+{
+}
+
+nes_waixing_sec_device::nes_waixing_sec_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_txrom_device(mconfig, NES_WAIXING_SEC, "NES Cart Waixing Security Chip PCB", tag, owner, clock, "nes_waixing_sec", __FILE__)
+{
+}
+
+nes_waixing_sgz_device::nes_waixing_sgz_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_WAIXING_SGZ, "NES Cart Waixing San Guo Zhi PCB", tag, owner, clock, "nes_waixing_sgz", __FILE__)
+{
+}
+
+nes_waixing_sgzlz_device::nes_waixing_sgzlz_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_WAIXING_SGZLZ, "NES Cart Waixing San Guo Zhong Lie Zhuan PCB", tag, owner, clock, "nes_waixing_sgzlz", __FILE__)
+{
+}
+
+nes_waixing_ffv_device::nes_waixing_ffv_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_WAIXING_FFV, "NES Cart Waixing Final Fantasy V PCB", tag, owner, clock, "nes_waixing_ffv", __FILE__)
+{
+}
+
+nes_waixing_wxzs_device::nes_waixing_wxzs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_WAIXING_WXZS, "NES Cart Waixing Wai Xing Zhan Shi PCB", tag, owner, clock, "nes_waixing_wxzs", __FILE__)
+{
+}
+
+nes_waixing_dq8_device::nes_waixing_dq8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_WAIXING_DQ8, "NES Cart Waixing Dragon Quest VIII PCB", tag, owner, clock, "nes_waixing_dq8", __FILE__)
+{
+}
+
+nes_waixing_wxzs2_device::nes_waixing_wxzs2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_WAIXING_WXZS2, "NES Cart Waixing Wai Xing Zhan Shi 2 PCB", tag, owner, clock, "nes_waixing_wxzs2", __FILE__)
+{
+}
+
+nes_waixing_fs304_device::nes_waixing_fs304_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : nes_nrom_device(mconfig, NES_WAIXING_FS304, "NES Cart Waixing FS-304 PCB", tag, owner, clock, "nes_waixing_fs304", __FILE__)
+{
+}
+
+
+void nes_waixing_a_device::device_start()
+{
+ mmc3_start();
+ save_item(NAME(mapper_ram));
+}
+
+void nes_waixing_a_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ mmc3_common_initialize(0xff, 0xff, 0);
+
+ memset(mapper_ram, 0, sizeof(mapper_ram));
+}
+
+void nes_waixing_f_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ mmc3_common_initialize(0xff, 0xff, 0);
+
+ memset(mapper_ram, 0, sizeof(mapper_ram));
+ m_mmc_prg_bank[0] = 0x00;
+ m_mmc_prg_bank[1] = 0x01;
+ m_mmc_prg_bank[2] = 0x4e;
+ m_mmc_prg_bank[3] = 0x4f;
+ set_prg(m_prg_base, m_prg_mask);
+}
+
+void nes_waixing_g_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ mmc3_common_initialize(0xff, 0xff, 0);
+
+ memset(mapper_ram, 0, sizeof(mapper_ram));
+ m_mmc_prg_bank[0] = 0x00;
+ m_mmc_prg_bank[1] = 0x01;
+ m_mmc_prg_bank[2] = 0x3e;
+ m_mmc_prg_bank[3] = 0x3f;
+ m_mmc_vrom_bank[0] = 0x00;
+ m_mmc_vrom_bank[1] = 0x02;
+ m_mmc_vrom_bank[2] = 0x04;
+ m_mmc_vrom_bank[3] = 0x05;
+ m_mmc_vrom_bank[4] = 0x06;
+ m_mmc_vrom_bank[5] = 0x07;
+ m_mmc_vrom_bank[6] = 0x01;
+ m_mmc_vrom_bank[7] = 0x03;
+ set_prg(m_prg_base, m_prg_mask);
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+}
+
+void nes_waixing_j_device::device_start()
+{
+ mmc3_start();
+ save_item(NAME(m_reg));
+}
+
+void nes_waixing_j_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+
+ m_reg[0] = 0x01;
+ m_reg[1] = 0x02;
+ m_reg[2] = 0x7e;
+ m_reg[3] = 0x7f;
+ mmc3_common_initialize(0xff, 0xff, 0);
+ set_prg(m_prg_base, m_prg_mask);
+}
+
+void nes_waixing_sh2_device::device_start()
+{
+ mmc3_start();
+ save_item(NAME(m_reg));
+}
+
+void nes_waixing_sh2_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+
+ m_reg[0] = m_reg[1] = 0;
+ mmc3_common_initialize(0xff, 0xff, 0);
+}
+
+void nes_waixing_sec_device::device_start()
+{
+ mmc3_start();
+ save_item(NAME(m_reg));
+}
+
+void nes_waixing_sec_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+
+ m_reg = 0;
+ mmc3_common_initialize(0xff, 0xff, 0);
+}
+
+void nes_waixing_sgz_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_irq_enable));
+ save_item(NAME(m_irq_enable_latch));
+ save_item(NAME(m_irq_count));
+ save_item(NAME(m_irq_count_latch));
+ save_item(NAME(m_mmc_vrom_bank));
+}
+
+void nes_waixing_sgz_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(m_prg_chunks - 1);
+ chr8(0, m_chr_source);
+
+ m_irq_enable = 0;
+ m_irq_enable_latch = 0;
+ m_irq_count = 0;
+ m_irq_count_latch = 0;
+
+ memset(m_mmc_vrom_bank, 0, sizeof(m_mmc_vrom_bank));
+}
+
+void nes_waixing_sgzlz_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_latch));
+}
+
+void nes_waixing_sgzlz_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+
+ m_latch = 0;
+}
+
+void nes_waixing_ffv_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_reg));
+}
+
+void nes_waixing_ffv_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg16_89ab(0);
+ prg16_cdef(0x1f);
+ chr8(0, m_chr_source);
+
+ m_reg[0] = m_reg[1] = 0;
+}
+
+void nes_waixing_wxzs_device::device_start()
+{
+ common_start();
+}
+
+void nes_waixing_wxzs_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_waixing_dq8_device::device_start()
+{
+ common_start();
+}
+
+void nes_waixing_dq8_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+}
+
+void nes_waixing_wxzs2_device::device_start()
+{
+ common_start();
+}
+
+void nes_waixing_wxzs2_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+ set_nt_mirroring(PPU_MIRROR_VERT);
+}
+
+void nes_waixing_fs304_device::device_start()
+{
+ common_start();
+ save_item(NAME(m_reg));
+}
+
+void nes_waixing_fs304_device::pcb_reset()
+{
+ m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM;
+ prg32(0);
+ chr8(0, m_chr_source);
+
+ memset(m_reg, 0x00, sizeof(m_reg));
+}
+
+
+
+
+
+/*-------------------------------------------------
+ mapper specific handlers
+ -------------------------------------------------*/
+
+/*-------------------------------------------------
+
+ Waixing Board Type A
+
+ Games: Columbus - Ougon no Yoake (C), Ji Jia Zhan Shi,
+ Jia A Fung Yun, Wei Luo Chuan Qi
+
+ This mapper is quite similar to MMC3, but with two differences:
+ mirroring is not the same, and when VROM banks 8,9 are accessed
+ they point to CHRRAM and not CHRROM.
+
+ iNES: mapper 74
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+/* MIRROR_LOW and MIRROR_HIGH are swapped! */
+void nes_waixing_a_device::set_mirror(UINT8 nt)
+{
+ switch (nt)
+ {
+ case 0:
+ case 1:
+ set_nt_mirroring(nt ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ break;
+ case 2:
+ set_nt_mirroring(PPU_MIRROR_LOW);
+ break;
+ case 3:
+ set_nt_mirroring(PPU_MIRROR_HIGH);
+ break;
+ default:
+ LOG_MMC(("Mapper set NT to invalid value %02x", nt));
+ break;
+ }
+}
+
+/* Luo Ke Ren X only works with this */
+void nes_waixing_a_device::chr_cb(int start, int bank, int source)
+{
+ int chr_src = (bank <= 9) ? CHRRAM : CHRROM;
+ chr1_x(start, bank, chr_src);
+}
+
+/* Ji Jia Zhan Shi only works with this */
+void nes_waixing_a1_device::chr_cb(int start, int bank, int source)
+{
+ int chr_src = ((bank == 8) || (bank == 9)) ? CHRRAM : CHRROM;
+ chr1_x(start, bank, chr_src);
+}
+
+WRITE8_MEMBER(nes_waixing_a_device::waixing_write)
+{
+ LOG_MMC(("waixing_write, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x6001)
+ {
+ case 0x2000:
+ set_mirror(data); //maybe data & 0x03?
+ break;
+
+ case 0x2001:
+ break;
+
+ default:
+ txrom_write(space, offset, data, mem_mask);
+ break;
+ }
+}
+
+READ8_MEMBER(nes_waixing_a_device::read_l)
+{
+ LOG_MMC(("waixing read_l, offset: %04x\n", offset));
+ offset += 0x100;
+ if (offset >= 0x1000 && offset < 0x1400)
+ return mapper_ram[offset & 0x3ff];
+ else
+ return 0xff;
+}
+
+WRITE8_MEMBER(nes_waixing_a_device::write_l)
+{
+ LOG_MMC(("waixing write_l, offset: %04x, data: %02x\n", offset, data));
+ offset += 0x100;
+ if (offset >= 0x1000 && offset < 0x1400)
+ mapper_ram[offset & 0x3ff] = data;
+}
+
+
+/*-------------------------------------------------
+
+ Waixing Board Type B
+
+ Games: Sugoro Quest (C)
+
+ MMC3 clone. This is a minor modification of Mapper 74,
+ in the sense that it is the same board except for the
+ CHRRAM pages.
+
+ iNES: mapper 191
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+void nes_waixing_b_device::chr_cb(int start, int bank, int source)
+{
+ int chr_src = BIT(bank, 7) ? CHRRAM : CHRROM;
+ chr1_x(start, bank, chr_src);
+}
+
+/*-------------------------------------------------
+
+ Waixing Board Type C
+
+ Games: Ying Lie Qun Xia Zhuan, Young Chivalry
+
+ MMC3 clone. This is a minor modification of Mapper 74,
+ in the sense that it is the same board except for the
+ CHRRAM pages.
+
+ iNES: mapper 192
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+void nes_waixing_c_device::chr_cb(int start, int bank, int source)
+{
+ int chr_src = ((bank == 0x08) || (bank == 0x09) || (bank == 0x0a) || (bank == 0x0b)) ? CHRRAM : CHRROM;
+ chr1_x(start, bank, chr_src);
+}
+
+/*-------------------------------------------------
+
+ Waixing Board Type D
+
+ Games: Super Robot Taisen (C)
+
+ MMC3 clone. This is a minor modification of Mapper 74,
+ in the sense that it is the same board except for the
+ CHRRAM pages.
+
+ iNES: mapper 194
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+void nes_waixing_d_device::chr_cb(int start, int bank, int source)
+{
+ int chr_src = (bank < 0x02) ? CHRRAM : CHRROM;
+ chr1_x(start, bank, chr_src);
+}
+
+/*-------------------------------------------------
+
+ Waixing Board Type E
+
+ Games: Captain Tsubasa Vol. II (C), Chaos World, God
+ Slayer (C), Zu Qiu Xiao Jiang
+
+ MMC3 clone. This is a minor modification of Mapper 74,
+ in the sense that it is the same board except for the
+ CHRRAM pages.
+
+ iNES: mapper 195
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+void nes_waixing_e_device::chr_cb(int start, int bank, int source)
+{
+ int chr_src = (bank < 0x04) ? CHRRAM : CHRROM;
+ chr1_x(start, bank, chr_src);
+}
+
+
+/*-------------------------------------------------
+
+ Waixing Board Type F
+
+ Games: Tenchi wo Kurau II (C)
+
+ MMC3 clone.
+
+ iNES: mapper 198
+
+ In MESS: Preliminary support.
+
+ -------------------------------------------------*/
+
+void nes_waixing_f_device::chr_cb(int start, int bank, int source)
+{
+ chr1_x(start, bank, CHRRAM);
+}
+
+void nes_waixing_f_device::prg_cb(int start, int bank)
+{
+// if (bank > 0x3f)
+// bank = 0x40 | (bank & 0xf);
+ prg8_x(start, bank);
+}
+
+void nes_waixing_f_device::set_prg( int prg_base, int prg_mask )
+{
+ UINT8 prg_flip = (m_latch & 0x40) ? 2 : 0;
+
+ prg_cb(0, m_mmc_prg_bank[0 ^ prg_flip]);
+ prg_cb(1, m_mmc_prg_bank[1]);
+ prg_cb(2, m_mmc_prg_bank[2 ^ prg_flip]);
+ prg_cb(3, m_mmc_prg_bank[3]);
+}
+
+WRITE8_MEMBER(nes_waixing_f_device::write_h)
+{
+ UINT8 cmd;
+ LOG_MMC(("waixing_f write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x6001)
+ {
+ case 0x0001:
+ cmd = m_latch & 0x07;
+ switch (cmd)
+ {
+ case 0: case 1: // these do not need to be separated: we take care of them in set_chr!
+ case 2: case 3: case 4: case 5:
+ m_mmc_vrom_bank[cmd] = data;
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+ break;
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ m_mmc_prg_bank[cmd - 6] = data;
+ //printf("prg bank %d value %x\n", cmd - 6, data);
+ set_prg(m_prg_base, m_prg_mask);
+ break;
+ }
+ break;
+
+ case 0x2001:
+ break;
+
+ default:
+ waixing_write(space, offset, data, mem_mask);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Waixing Board Type G
+
+ Games: San Guo Zhi 2, Dragon Ball Z Gaiden (C), Dragon
+ Ball Z II (C)
+
+ MMC3 clone, capable of switching all 4 PRG banks
+
+ iNES: mapper 199
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+void nes_waixing_g_device::chr_cb(int start, int bank, int source)
+{
+ int chr_src = (bank < 0x08) ? CHRRAM : CHRROM;
+ chr1_x(start, bank, chr_src);
+}
+
+void nes_waixing_g_device::set_chr(UINT8 chr, int chr_base, int chr_mask)
+{
+ UINT8 chr_page = (m_latch & 0x80) >> 5;
+
+ chr_cb(chr_page ^ 0, chr_base | (m_mmc_vrom_bank[0] & chr_mask), chr);
+ chr_cb(chr_page ^ 1, chr_base | (m_mmc_vrom_bank[6] & chr_mask), chr);
+ chr_cb(chr_page ^ 2, chr_base | (m_mmc_vrom_bank[1] & chr_mask), chr);
+ chr_cb(chr_page ^ 3, chr_base | (m_mmc_vrom_bank[7] & chr_mask), chr);
+ chr_cb(chr_page ^ 4, chr_base | (m_mmc_vrom_bank[2] & chr_mask), chr);
+ chr_cb(chr_page ^ 5, chr_base | (m_mmc_vrom_bank[3] & chr_mask), chr);
+ chr_cb(chr_page ^ 6, chr_base | (m_mmc_vrom_bank[4] & chr_mask), chr);
+ chr_cb(chr_page ^ 7, chr_base | (m_mmc_vrom_bank[5] & chr_mask), chr);
+}
+
+WRITE8_MEMBER(nes_waixing_g_device::write_h)
+{
+ UINT8 cmd;
+ LOG_MMC(("waixing_g write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x6001)
+ {
+ case 0x0001:
+ cmd = m_latch & 0x0f;
+ switch (cmd)
+ {
+ case 0: case 1:
+ case 2: case 3: case 4: case 5:
+ m_mmc_vrom_bank[cmd] = data;
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+ break;
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ m_mmc_prg_bank[cmd - 6] = data;
+ set_prg(m_prg_base, m_prg_mask);
+ break;
+ case 0x0a: case 0x0b:
+ m_mmc_vrom_bank[cmd - 4] = data;
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+ break;
+ }
+ break;
+
+ default:
+ waixing_write(space, offset, data, mem_mask);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Waixing Board Type H
+
+ Games: Ying Xiong Yuan Yi Jing Chuan Qi, Yong Zhe Dou E
+ Long - Dragon Quest VII
+
+ MMC3 clone. More info to come.
+
+ Notice that Chinese Zelda translation stops working if
+ WRAM protect bit is ignored (i.e. if writes to 0x2001
+ are skipped)! OTOH, Chinese Monster Maker 1/2 translations
+ and Zheng Ba ShiJi stop working if WRAM protect is
+ accounted. So we split the board into two subtypes
+
+ iNES: mapper 245
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+void nes_waixing_h_device::chr_cb(int start, int bank, int source)
+{
+ if (source == CHRROM)
+ chr1_x(start, bank, source);
+}
+
+WRITE8_MEMBER(nes_waixing_h_device::write_h)
+{
+ UINT8 cmd;
+ LOG_MMC(("waixing_h write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x6001)
+ {
+ case 0x0001:
+ cmd = m_latch & 0x07;
+ switch (cmd)
+ {
+ case 0: // in this case we set prg_base in addition to m_mmc_vrom_bank!
+ m_prg_base = (data << 5) & 0x40;
+ m_prg_mask = 0x3f;
+ set_prg(m_prg_base, m_prg_mask);
+ case 1:
+ case 2: case 3: case 4: case 5:
+ m_mmc_vrom_bank[cmd] = data;
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+ break;
+ case 6:
+ case 7:
+ m_mmc_prg_bank[cmd - 6] = data;
+ set_prg(m_prg_base, m_prg_mask);
+ break;
+ case 0x0a: case 0x0b: // CHR switches in 1K banks only, and bank1 & bank3 are controlled by these
+ m_mmc_vrom_bank[cmd - 4] = data;
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+ break;
+ }
+ break;
+
+ case 0x2001:
+ break;
+
+ default:
+ txrom_write(space, offset, data, mem_mask);
+ break;
+ }
+}
+
+WRITE8_MEMBER(nes_waixing_h1_device::write_h)
+{
+ UINT8 cmd;
+ LOG_MMC(("waixing_h1 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x6001)
+ {
+ case 0x0001:
+ cmd = m_latch & 0x07;
+ switch (cmd)
+ {
+ case 0: // in this case we set prg_base in addition to m_mmc_vrom_bank!
+ m_prg_base = (data << 5) & 0x40;
+ m_prg_mask = 0x3f;
+ set_prg(m_prg_base, m_prg_mask);
+ case 1:
+ case 2: case 3: case 4: case 5:
+ m_mmc_vrom_bank[cmd] = data;
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+ break;
+ case 6:
+ case 7:
+ m_mmc_prg_bank[cmd - 6] = data;
+ set_prg(m_prg_base, m_prg_mask);
+ break;
+ case 0x0a: case 0x0b: // CHR switches in 1K banks only, and bank1 & bank3 are controlled by these
+ m_mmc_vrom_bank[cmd - 4] = data;
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+ break;
+ }
+ break;
+
+ default:
+ txrom_write(space, offset, data, mem_mask);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Waixing Board Type J
+
+ Games: Final Fantasy III (C)
+
+ MMC3 clone.
+
+ In MESS: Preliminary support.
+
+ -------------------------------------------------*/
+
+void nes_waixing_j_device::set_prg( int prg_base, int prg_mask )
+{
+ UINT8 prg_flip = (m_latch & 0x40) ? 2 : 0;
+
+ prg_cb(0, m_reg[0 ^ prg_flip]);
+ prg_cb(1, m_reg[1]);
+ prg_cb(2, m_reg[2 ^ prg_flip]);
+ prg_cb(3, m_reg[3]);
+}
+
+WRITE8_MEMBER(nes_waixing_j_device::write_h)
+{
+ UINT8 cmd;
+ LOG_MMC(("waixing_f write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x6001)
+ {
+ case 0x0001:
+ cmd = m_latch & 0x07;
+ switch (cmd)
+ {
+ case 0: case 1: // these do not need to be separated: we take care of them in set_chr!
+ case 2: case 3: case 4: case 5:
+ m_mmc_vrom_bank[cmd] = data;
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+ break;
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ m_reg[cmd - 6] = data;
+ set_prg(m_prg_base, m_prg_mask);
+ break;
+ }
+ break;
+
+// case 0x2001:
+// break;
+
+ default:
+ waixing_write(space, offset, data, mem_mask);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Waixing SH2 Board
+
+ Games: Fire Emblem (C) and Fire Emblem Gaiden (C)
+
+ MMC3 clone with different access to CHR
+
+ iNES: mapper 165
+
+ In MESS: Partially Supported.
+
+ -------------------------------------------------*/
+
+void nes_waixing_sh2_device::chr_cb(int start, int bank, int source)
+{
+ chr4_0(m_reg[0], m_reg[0] ? CHRRAM : CHRROM);
+ chr4_4(m_reg[1], m_reg[1] ? CHRRAM : CHRROM);
+}
+
+READ8_MEMBER(nes_waixing_sh2_device::chr_r)
+{
+ int bank = offset >> 10;
+ UINT8 val = m_chr_access[bank][offset & 0x3ff]; // this would be usual return value
+ int chr_helper;
+
+ switch (offset & 0xff8)
+ {
+ case 0xfd0: chr_helper = (bank & 0x4) | 0x0; break;
+ case 0xfe8: chr_helper = (bank & 0x4) | 0x2; break;
+ default: return val;
+ }
+
+ m_reg[offset >> 12] = chr_helper;
+ if (offset & 0x1000)
+ chr4_4(m_reg[1], m_reg[1] ? CHRRAM : CHRROM);
+ else
+ chr4_0(m_reg[0], m_reg[0] ? CHRRAM : CHRROM);
+
+ return val;
+}
+
+/*-------------------------------------------------
+
+ Waixing Board with Security Chip
+
+ Games: Duo Bao Xiao Ying Hao - Guang Ming yu An Hei Chuan Shuo,
+ Myth Struggle, San Shi Liu Ji, Shui Hu Zhuan
+
+ MMC3 clone
+
+ iNES: mapper 249
+
+ In MESS: Partially Supported.
+
+ -------------------------------------------------*/
+
+void nes_waixing_sec_device::prg_cb(int start, int bank)
+{
+ if (m_reg)
+ bank = BITSWAP8(bank & 0x1f,7,6,5,2,1,3,4,0);
+
+ prg8_x(start, bank);
+}
+
+void nes_waixing_sec_device::chr_cb(int start, int bank, int source)
+{
+ if (m_reg)
+ bank = BITSWAP8(bank, 5,4,2,6,7,3,1,0);
+
+ chr1_x(start, bank, source);
+}
+
+WRITE8_MEMBER(nes_waixing_sec_device::write_l)
+{
+ LOG_MMC(("waixing_sec write_l, offset: %04x, data: %02x\n", offset, data));
+ offset += 0x100;
+
+ if (offset == 0x1000)
+ {
+ m_reg = data & 0x02;
+ set_prg(m_prg_base, m_prg_mask);
+ set_chr(m_chr_source, m_chr_base, m_chr_mask);
+ }
+}
+
+/*-------------------------------------------------
+
+ Waixing San Guo Zhi Board
+
+ Games: San Guo Zhi
+
+ This board uses Konami IRQ
+
+ iNES: mapper 252
+
+ In MESS: Unsupported.
+
+ -------------------------------------------------*/
+
+// same as Konami IRQ
+void nes_waixing_sgz_device::hblank_irq(int scanline, int vblank, int blanked)
+{
+ /* Increment & check the IRQ scanline counter */
+ if (m_irq_enable && (++m_irq_count == 0x100))
+ {
+ m_irq_count = m_irq_count_latch;
+ m_irq_enable = m_irq_enable_latch;
+ m_maincpu->set_input_line(M6502_IRQ_LINE, HOLD_LINE);
+ }
+}
+
+WRITE8_MEMBER(nes_waixing_sgz_device::write_h)
+{
+ UINT8 helper, bank;
+ LOG_MMC(("waixing_sgz write_h, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset & 0x7000)
+ {
+ case 0x0000:
+ prg8_89(data);
+ break;
+ case 0x2000:
+ prg8_ab(data);
+ break;
+ case 0x3000:
+ case 0x4000:
+ case 0x5000:
+ case 0x6000:
+ bank = ((offset & 0x7000) - 0x3000) / 0x0800 + ((offset & 0x0008) >> 3);
+ helper = offset & 0x04;
+ if (helper)
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0x0f) | ((data & 0x0f) << 4);
+ else
+ m_mmc_vrom_bank[bank] = (m_mmc_vrom_bank[bank] & 0xf0) | (data & 0x0f);
+ chr1_x(bank, m_mmc_vrom_bank[bank], CHRROM);
+ break;
+ case 0x7000:
+ switch (offset & 0x0c)
+ {
+ case 0x00:
+ m_irq_count_latch = (m_irq_count_latch & 0xf0) | (data & 0x0f);
+ break;
+ case 0x04:
+ m_irq_count_latch = (m_irq_count_latch & 0x0f) | ((data & 0x0f) << 4);
+ break;
+ case 0x08:
+ m_irq_enable = data & 0x02;
+ m_irq_count_latch = data & 0x01;
+ if (data & 0x02)
+ m_irq_count = m_irq_count_latch;
+ break;
+ case 0x0c:
+ m_irq_enable = m_irq_enable_latch;
+ break;
+ }
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+
+ Waixing San Guo Zhong Lie Zhuan Board
+
+ Games: Fan Kong Jing Ying, San Guo Zhong Lie Zhuan, Xing
+ Ji Zheng Ba
+
+ iNES: mapper 178
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_waixing_sgzlz_device::write_l)
+{
+ LOG_MMC(("waixing_sgzlz write_l, offset: %04x, data: %02x\n", offset, data));
+
+ switch (offset)
+ {
+ case 0x700:
+ set_nt_mirroring(data ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+ break;
+ case 0x701:
+ m_latch = (m_latch & 0x0c) | ((data >> 1) & 0x03);
+ prg32(m_latch);
+ break;
+ case 0x702:
+ m_latch = (m_latch & 0x03) | ((data << 2) & 0x0c);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Waixing Final Fantasy V Board
+
+ Games: Darkseed, Digital Dragon, Final Fantasy V, Pocket
+ Monster Red
+
+ iNES: mapper 164
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_waixing_ffv_device::write_l)
+{
+ UINT8 helper;
+ LOG_MMC(("waixing_ffv write_l, offset: %04x, data: %02x\n", offset, data));
+ offset += 0x100; /* the checks work better on addresses */
+
+ if ((offset & 0x1200) == 0x1000)
+ {
+ m_reg[BIT(offset, 8)] = data;
+ helper = BIT(m_reg[1], 0) << 5;
+ switch (m_reg[0] & 0x70)
+ {
+ case 0x00:
+ case 0x20:
+ case 0x40:
+ case 0x60:
+ prg16_89ab(helper | ((m_reg[0] >> 1) & 0x10) | (m_reg[0] & 0x0f));
+ prg16_cdef(helper & 0x1f);
+ break;
+ case 0x50:
+ prg32((helper >> 1) | (m_reg[0] & 0x0f));
+ break;
+ case 0x70:
+ prg16_89ab(helper | ((m_reg[0] << 1) & 0x10) | (m_reg[0] & 0x0f));
+ prg16_cdef(helper & 0x1f);
+ break;
+ }
+ }
+}
+
+/*-------------------------------------------------
+
+ Waixing Zhan Shi Board
+
+ Games: Wai Xing Zhan Shi
+
+ Simple mapper: writes to 0x8000-0xffff sets prg32 banks to
+ (offset>>3)&f. written data&3 sets the mirroring (with
+ switched high/low compared to the standard one).
+
+ A crc check is required to support Dragon Quest VIII (which
+ uses a slightly different board)
+
+ iNES: mapper 242
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_waixing_wxzs_device::write_h)
+{
+ LOG_MMC(("waixing_zs write_h, offset: %04x, data: %02x\n", offset, data));
+
+ prg32(offset >> 3);
+
+ switch (data & 0x03)
+ {
+ case 0: set_nt_mirroring(PPU_MIRROR_VERT); break;
+ case 1: set_nt_mirroring(PPU_MIRROR_HORZ); break;
+ case 2: set_nt_mirroring(PPU_MIRROR_LOW); break;
+ case 3: set_nt_mirroring(PPU_MIRROR_HIGH); break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Waixing Dragon Quest VIII Board
+
+ Games: Dragon Quest VIII
+
+ Simple mapper: writes to 0x8000-0xffff sets prg32 banks to
+ (offset>>3)&f.
+
+ iNES: mapper 242
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_waixing_dq8_device::write_h)
+{
+ LOG_MMC(("waixing_dq8 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ prg32(offset >> 3);
+}
+
+
+/*-------------------------------------------------
+
+ Waixing WXZS2 / PS2 board
+
+ Games: Wai Xing Zhan Shi 2 (aka Phantasy Star 2),
+ Bao Xiao Tien Guo, Bio Hazard, Pokemon Gold, Subor (R)
+
+ iNES: mapper 15
+
+ In MESS: Supported
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_waixing_wxzs2_device::write_h)
+{
+ UINT8 flip = (data & 0x80) >> 7;
+ UINT8 helper = (data & 0x7f) << 1;
+
+ LOG_MMC(("waixing_wxzs2 write_h, offset: %04x, data: %02x\n", offset, data));
+
+ set_nt_mirroring(BIT(data, 6) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT);
+
+ switch (offset & 0x0fff)
+ {
+ case 0x000:
+ prg8_89((helper + 0) ^ flip);
+ prg8_ab((helper + 1) ^ flip);
+ prg8_cd((helper + 2) ^ flip);
+ prg8_ef((helper + 3) ^ flip);
+ break;
+ case 0x001:
+ helper |= flip;
+ prg8_89(helper);
+ prg8_ab(helper + 1);
+ prg8_cd(helper + 1);
+ prg8_ef(helper + 1);
+ break;
+ case 0x002:
+ helper |= flip;
+ prg8_89(helper);
+ prg8_ab(helper);
+ prg8_cd(helper);
+ prg8_ef(helper);
+ break;
+ case 0x003:
+ helper |= flip;
+ prg8_89(helper);
+ prg8_ab(helper + 1);
+ prg8_cd(helper);
+ prg8_ef(helper + 1);
+ break;
+ }
+}
+
+/*-------------------------------------------------
+
+ Board UNL-FS304
+
+ Games: A Link to the Past by Waixing
+
+ iNES: mapper 162? (only found in UNIF format)
+
+ In MESS: Supported.
+
+ -------------------------------------------------*/
+
+WRITE8_MEMBER(nes_waixing_fs304_device::write_l)
+{
+ LOG_MMC(("fs304 write_l, offset: %04x, data: %02x\n", offset, data));
+ int bank;
+ offset += 0x100;
+
+ if (offset >= 0x1000)
+ {
+ m_reg[(offset >> 8) & 3] = data;
+ bank = ((m_reg[2] & 0x0f) << 4) | BIT(m_reg[1], 1) | (m_reg[0] & 0x0e);
+ prg32(bank);
+ chr8(0, CHRRAM);
+ }
+}
diff --git a/src/devices/bus/nes/waixing.h b/src/devices/bus/nes/waixing.h
new file mode 100644
index 00000000000..42220c5523d
--- /dev/null
+++ b/src/devices/bus/nes/waixing.h
@@ -0,0 +1,396 @@
+// license:BSD-3-Clause
+// copyright-holders:Fabio Priuli
+#ifndef __NES_WAIXING_H
+#define __NES_WAIXING_H
+
+#include "mmc3.h"
+
+
+// ======================> nes_waixing_a_device
+
+class nes_waixing_a_device : public nes_txrom_device
+{
+public:
+ // construction/destruction
+ nes_waixing_a_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_waixing_a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(read_l);
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+ virtual DECLARE_WRITE8_MEMBER(waixing_write);
+ virtual DECLARE_WRITE8_MEMBER(write_h) { waixing_write(space, offset, data, mem_mask); }
+ virtual void chr_cb(int start, int bank, int source);
+
+ virtual void pcb_reset();
+
+protected:
+ void set_mirror(UINT8 nt);
+ UINT8 mapper_ram[0x400];
+};
+
+
+// ======================> nes_waixing_a1_device
+
+class nes_waixing_a1_device : public nes_waixing_a_device
+{
+public:
+ // construction/destruction
+ nes_waixing_a1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ virtual void chr_cb(int start, int bank, int source);
+};
+
+
+// ======================> nes_waixing_b_device
+
+class nes_waixing_b_device : public nes_waixing_a_device
+{
+public:
+ // construction/destruction
+ nes_waixing_b_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void chr_cb(int start, int bank, int source);
+};
+
+
+// ======================> nes_waixing_c_device
+
+class nes_waixing_c_device : public nes_waixing_a_device
+{
+public:
+ // construction/destruction
+ nes_waixing_c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void chr_cb(int start, int bank, int source);
+};
+
+
+// ======================> nes_waixing_d_device
+
+class nes_waixing_d_device : public nes_waixing_a_device
+{
+public:
+ // construction/destruction
+ nes_waixing_d_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void chr_cb(int start, int bank, int source);
+};
+
+
+// ======================> nes_waixing_e_device
+
+class nes_waixing_e_device : public nes_waixing_a_device
+{
+public:
+ // construction/destruction
+ nes_waixing_e_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void chr_cb(int start, int bank, int source);
+};
+
+
+// ======================> nes_waixing_f_device
+
+class nes_waixing_f_device : public nes_waixing_a_device
+{
+public:
+ // construction/destruction
+ nes_waixing_f_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+ virtual void prg_cb(int start, int bank);
+ virtual void chr_cb(int start, int bank, int source);
+
+ virtual void pcb_reset();
+private:
+ virtual void set_prg(int prg_base, int prg_mask);
+};
+
+
+// ======================> nes_waixing_g_device
+
+class nes_waixing_g_device : public nes_waixing_a_device
+{
+public:
+ // construction/destruction
+ nes_waixing_g_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+ virtual void chr_cb(int start, int bank, int source);
+
+ virtual void pcb_reset();
+
+protected:
+ virtual void set_chr(UINT8 chr, int chr_base, int chr_mask);
+};
+
+
+// ======================> nes_waixing_h_device
+
+class nes_waixing_h_device : public nes_txrom_device
+{
+public:
+ // construction/destruction
+ nes_waixing_h_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source);
+ nes_waixing_h_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+ virtual void chr_cb(int start, int bank, int source);
+
+ // This PCB does not have 1K of internal RAM, so it's not derived from nes_waixing_a_device!!
+};
+
+
+// ======================> nes_waixing_h1_device
+
+class nes_waixing_h1_device : public nes_waixing_h_device
+{
+public:
+ // construction/destruction
+ nes_waixing_h1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ // This variant does not ignore the wram protect!
+};
+
+
+// ======================> nes_waixing_i_device
+
+class nes_waixing_i_device : public nes_waixing_a_device
+{
+public:
+ // construction/destruction
+ nes_waixing_i_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // still to emulate this variant
+};
+
+
+// ======================> nes_waixing_j_device
+
+class nes_waixing_j_device : public nes_waixing_a_device
+{
+public:
+ // construction/destruction
+ nes_waixing_j_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+
+protected:
+ virtual void set_prg(int prg_base, int prg_mask);
+ UINT8 m_reg[4];
+};
+
+
+// ======================> nes_waixing_sh2_device
+
+class nes_waixing_sh2_device : public nes_txrom_device
+{
+public:
+ // construction/destruction
+ nes_waixing_sh2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_READ8_MEMBER(chr_r);
+ virtual void chr_cb(int start, int bank, int source);
+
+ virtual void pcb_reset();
+
+protected:
+ UINT8 m_reg[2];
+};
+
+
+// ======================> nes_waixing_sec_device
+
+class nes_waixing_sec_device : public nes_txrom_device
+{
+public:
+ // construction/destruction
+ nes_waixing_sec_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+ virtual void prg_cb(int start, int bank);
+ virtual void chr_cb(int start, int bank, int source);
+
+ virtual void pcb_reset();
+
+protected:
+ UINT8 m_reg;
+};
+
+
+// ======================> nes_waixing_sgz_device
+
+class nes_waixing_sgz_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_waixing_sgz_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void hblank_irq(int scanline, int vblank, int blanked);
+ virtual void pcb_reset();
+
+protected:
+ UINT16 m_irq_count, m_irq_count_latch;
+ int m_irq_enable, m_irq_enable_latch;
+
+ UINT8 m_mmc_vrom_bank[8];
+};
+
+
+// ======================> nes_waixing_sgzlz_device
+
+class nes_waixing_sgzlz_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_waixing_sgzlz_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+
+ virtual void pcb_reset();
+
+protected:
+ UINT8 m_latch;
+};
+
+
+// ======================> nes_waixing_ffv_device
+
+class nes_waixing_ffv_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_waixing_ffv_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+
+ virtual void pcb_reset();
+
+protected:
+ UINT8 m_reg[2];
+};
+
+
+// ======================> nes_waixing_wxzs_device
+
+class nes_waixing_wxzs_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_waixing_wxzs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_waixing_dq8_device
+
+class nes_waixing_dq8_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_waixing_dq8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_waixing_wxzs2_device
+
+class nes_waixing_wxzs2_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_waixing_wxzs2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_h);
+
+ virtual void pcb_reset();
+};
+
+
+// ======================> nes_waixing_fs304_device
+
+class nes_waixing_fs304_device : public nes_nrom_device
+{
+public:
+ // construction/destruction
+ nes_waixing_fs304_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
+
+ // device-level overrides
+ virtual void device_start();
+ virtual DECLARE_WRITE8_MEMBER(write_l);
+
+ virtual void pcb_reset();
+
+protected:
+ UINT8 m_reg[4];
+};
+
+
+
+
+
+// device type definition
+extern const device_type NES_WAIXING_A;
+extern const device_type NES_WAIXING_A1;
+extern const device_type NES_WAIXING_B;
+extern const device_type NES_WAIXING_C;
+extern const device_type NES_WAIXING_D;
+extern const device_type NES_WAIXING_E;
+extern const device_type NES_WAIXING_F;
+extern const device_type NES_WAIXING_G;
+extern const device_type NES_WAIXING_H;
+extern const device_type NES_WAIXING_H1;
+extern const device_type NES_WAIXING_I;
+extern const device_type NES_WAIXING_J;
+extern const device_type NES_WAIXING_SH2;
+extern const device_type NES_WAIXING_SEC;
+extern const device_type NES_WAIXING_SGZ;
+extern const device_type NES_WAIXING_SGZLZ;
+extern const device_type NES_WAIXING_FFV;
+extern const device_type NES_WAIXING_WXZS;
+extern const device_type NES_WAIXING_DQ8;
+extern const device_type NES_WAIXING_WXZS2;
+extern const device_type NES_WAIXING_FS304;
+
+
+#endif