summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/nes/ggenie.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/nes/ggenie.c')
-rw-r--r--src/devices/bus/nes/ggenie.c274
1 files changed, 274 insertions, 0 deletions
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 );
+}