From 1039cc50df65effe584c12f3b5c12756e3ab3689 Mon Sep 17 00:00:00 2001
From: 0kmg <9137159+0kmg@users.noreply.github.com>
Date: Sat, 2 Apr 2022 12:47:01 -0800
Subject: [PATCH] bus/nes: Game Genie cleanup time. (#9502)
- Trimmed overdumped PRG ROM to 4K.
- Removed nonexistent CHR ROM, replaced with emulation of on-board logic.
- Removed hack from NES slot code that directly set the CPU program counter.
- Corrected reset behavior.
---
hash/nes.xml | 6 +-
src/devices/bus/nes/ggenie.cpp | 240 +++++++++++++++----------------
src/devices/bus/nes/ggenie.h | 38 ++---
src/devices/bus/nes/nes_slot.cpp | 6 -
src/devices/bus/nes/nes_slot.h | 1 -
5 files changed, 134 insertions(+), 157 deletions(-)
diff --git a/hash/nes.xml b/hash/nes.xml
index 4c93608d036..ddedf5c7bbe 100644
--- a/hash/nes.xml
+++ b/hash/nes.xml
@@ -44224,11 +44224,7 @@ license:CC0
-
-
-
-
-
+
diff --git a/src/devices/bus/nes/ggenie.cpp b/src/devices/bus/nes/ggenie.cpp
index bb5866feac7..bddebcad512 100644
--- a/src/devices/bus/nes/ggenie.cpp
+++ b/src/devices/bus/nes/ggenie.cpp
@@ -10,6 +10,8 @@
* Galoob Game Genie, passthrough hacking cart
+ TODO: emulate bugs/quirks/bus conflicts. See NesDev wiki.
+
***********************************************************************************************************/
@@ -33,10 +35,10 @@
DEFINE_DEVICE_TYPE(NES_GGENIE, nes_ggenie_device, "nes_ggenie", "NES Cart Game Genie PCB")
-nes_ggenie_device::nes_ggenie_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+nes_ggenie_device::nes_ggenie_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: nes_nrom_device(mconfig, NES_GGENIE, tag, owner, clock)
, m_ggslot(*this, "gg_slot")
- , m_gg_bypass(0)
+ , m_gg_bypass(false)
{
}
@@ -45,26 +47,35 @@ void nes_ggenie_device::device_start()
{
common_start();
save_item(NAME(m_gg_bypass));
+ save_item(NAME(m_gg_addr));
+ save_item(NAME(m_gg_repl));
+ save_item(NAME(m_gg_comp));
+ save_item(NAME(m_gg_enable));
+ save_item(NAME(m_gg_is_comp));
}
-void nes_ggenie_device::pcb_start(running_machine &machine, uint8_t *ciram_ptr, bool cart_mounted)
+void nes_ggenie_device::pcb_start(running_machine &machine, u8 *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);
+ m_ggslot->pcb_start(m_ciram);
+
+ prg32(0);
+ m_gg_bypass = false;
+
+ for (int i = 0; i < 3; i++)
+ {
+ m_gg_addr[i] = 0;
+ m_gg_repl[i] = 0;
+ m_gg_comp[i] = 0;
+ m_gg_enable[i] = false;
+ m_gg_is_comp[i] = false;
+ }
}
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();
+ // Game Genie does NOT reset to its interface once in game mode
+ m_ggslot->pcb_reset();
}
@@ -80,177 +91,154 @@ void nes_ggenie_device::pcb_reset()
-------------------------------------------------*/
-void nes_ggenie_device::write_h(offs_t offset, uint8_t data)
+void nes_ggenie_device::write_h(offs_t offset, u8 data)
{
-// LOG_MMC(("axrom write_h, offset: %04x, data: %02x\n", offset, data));
- if (!m_gg_bypass)
+// LOG_MMC(("ggenie 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!
+ m_ggslot->write_h(offset, data);
+ return;
+ }
- 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;
+ // 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, and disables the boot ROM. The second write to $8000 is always 0x00, and
+ // goes to the slave cart for unknown reasons.
+ // 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)
+ {
+ offset -= 1;
+ int code = BIT(offset, 2, 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;
}
-
- if (offset == 0 && data == 0)
+ }
+ else // 0x8000
+ {
+ m_gg_bypass = BIT(data, 0);
+ for (int i = 0; i < 3; i++)
{
- m_gg_bypass = 1;
- reset_cpu();
+ m_gg_is_comp[i] = BIT(data, i + 1);
+ m_gg_enable[i] = !BIT(data, i + 4);
}
- 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]);
+ // bit 7 is unused and always zero
- }
+ logerror("Game Genie Summary:\n");
+ for (int i = 0; i < 3; i++)
+ {
+ logerror("Code %d: %s\n", i, m_gg_enable[i] ? "Yes" : "No");
+ if (m_gg_enable[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(offset, data);
}
-void nes_ggenie_device::write_m(offs_t offset, uint8_t data)
+void nes_ggenie_device::write_m(offs_t offset, u8 data)
{
- if (m_gg_bypass && m_ggslot)
+ if (m_gg_bypass)
m_ggslot->write_m(offset, data);
}
-void nes_ggenie_device::write_l(offs_t offset, uint8_t data)
+void nes_ggenie_device::write_l(offs_t offset, u8 data)
{
- if (m_gg_bypass && m_ggslot)
+ if (m_gg_bypass)
m_ggslot->write_l(offset, data);
}
-uint8_t nes_ggenie_device::read_h(offs_t offset)
+u8 nes_ggenie_device::read_h(offs_t offset)
{
if (m_gg_bypass && m_ggslot->m_cart)
{
- uint8_t rom_value = m_ggslot->m_cart->hi_access_rom(offset);
+ u8 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_enable[i] && offset == m_gg_addr[i])
{
- if (!m_gg_is_comp[i] || (m_gg_is_comp[i] && m_gg_comp[i] == rom_value))
+ if (!m_gg_is_comp[i] || m_gg_comp[i] == rom_value)
return m_gg_repl[i];
}
}
- return rom_value;
+ return rom_value;
}
- return hi_access_rom(offset);
+ return m_prg[offset & 0xfff]; // ROM is only 4K
}
-uint8_t nes_ggenie_device::read_m(offs_t offset)
+u8 nes_ggenie_device::read_m(offs_t offset)
{
- if (m_gg_bypass && m_ggslot->m_cart)
- return m_ggslot->m_cart->read_m(offset);
+ if (m_gg_bypass)
+ return m_ggslot->read_m(offset);
return 0xff;
}
-uint8_t nes_ggenie_device::read_l(offs_t offset)
+u8 nes_ggenie_device::read_l(offs_t offset)
{
- if (m_gg_bypass && m_ggslot->m_cart)
- return m_ggslot->m_cart->read_l(offset);
+ if (m_gg_bypass)
+ return m_ggslot->read_l(offset);
return 0xff;
}
-void nes_ggenie_device::chr_w(offs_t offset, uint8_t data)
+void nes_ggenie_device::chr_w(offs_t offset, u8 data)
{
- int bank = offset >> 10;
-
if (m_gg_bypass && m_ggslot->m_cart)
- {
m_ggslot->m_cart->chr_w(offset, data);
- return;
- }
-
- if (m_chr_src[bank] == CHRRAM)
- m_chr_access[bank][offset & 0x3ff] = data;
}
-uint8_t nes_ggenie_device::chr_r(offs_t offset)
+u8 nes_ggenie_device::chr_r(offs_t offset)
{
- int bank = offset >> 10;
-
- if (m_gg_bypass && m_ggslot->m_cart)
- return m_ggslot->m_cart->chr_r(offset);
-
- return m_chr_access[bank][offset & 0x3ff];
-}
-
-
-void nes_ggenie_device::nt_w(offs_t offset, uint8_t data)
-{
- int page = ((offset & 0xc00) >> 10);
-
if (m_gg_bypass && m_ggslot->m_cart)
+ return m_ggslot->m_cart->chr_r(offset);
+ else
{
- m_ggslot->m_cart->nt_w(offset, data);
- return;
+ // there is no on-board CHRROM, 1 of 4 values are generated based on PPU A2, A4, A5, A6, A7, resulting in 16 tiles
+ static constexpr u8 chr_lut[4] = { 0x00, 0xf0, 0x0f, 0xff };
+ return chr_lut[BIT(offset, BIT(offset, 2) ? 4 : 6, 2)];
}
-
- if (!m_nt_writable[page])
- return;
-
- m_nt_access[page][offset & 0x3ff] = data;
}
-uint8_t nes_ggenie_device::nt_r(offs_t offset)
-{
- int page = ((offset & 0xc00) >> 10);
+void nes_ggenie_device::nt_w(offs_t offset, u8 data)
+{
+ if (m_gg_bypass && m_ggslot->m_cart)
+ m_ggslot->m_cart->nt_w(offset, data);
+ else
+ device_nes_cart_interface::nt_w(offset, data);
+}
+
+u8 nes_ggenie_device::nt_r(offs_t offset)
+{
if (m_gg_bypass && m_ggslot->m_cart)
return m_ggslot->m_cart->nt_r(offset);
-
- return m_nt_access[page][offset & 0x3ff];
+ else
+ return device_nes_cart_interface::nt_r(offset);
}
diff --git a/src/devices/bus/nes/ggenie.h b/src/devices/bus/nes/ggenie.h
index 2d6974be849..49681c1ed5f 100644
--- a/src/devices/bus/nes/ggenie.h
+++ b/src/devices/bus/nes/ggenie.h
@@ -14,26 +14,26 @@ 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_t clock);
+ nes_ggenie_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
- virtual uint8_t read_l(offs_t offset) override;
- virtual uint8_t read_m(offs_t offset) override;
- virtual uint8_t read_h(offs_t offset) override;
- virtual void write_l(offs_t offset, uint8_t data) override;
- virtual void write_m(offs_t offset, uint8_t data) override;
- virtual void write_h(offs_t offset, uint8_t data) override;
+ virtual u8 read_l(offs_t offset) override;
+ virtual u8 read_m(offs_t offset) override;
+ virtual u8 read_h(offs_t offset) override;
+ virtual void write_l(offs_t offset, u8 data) override;
+ virtual void write_m(offs_t offset, u8 data) override;
+ virtual void write_h(offs_t offset, u8 data) override;
- virtual uint8_t chr_r(offs_t offset) override;
- virtual void chr_w(offs_t offset, uint8_t data) override;
- virtual uint8_t nt_r(offs_t offset) override;
- virtual void nt_w(offs_t offset, uint8_t data) override;
+ virtual u8 chr_r(offs_t offset) override;
+ virtual void chr_w(offs_t offset, u8 data) override;
+ virtual u8 nt_r(offs_t offset) override;
+ virtual void nt_w(offs_t offset, u8 data) override;
void hblank_irq(int scanline, int vblank, int blanked) override { 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) override { if (m_gg_bypass && m_ggslot->m_cart) m_ggslot->m_cart->scanline_irq(scanline, vblank, blanked); }
void ppu_latch(offs_t offset) override { if (m_gg_bypass && m_ggslot->m_cart) m_ggslot->m_cart->ppu_latch(offset); }
virtual void pcb_reset() override;
- virtual void pcb_start(running_machine &machine, uint8_t *ciram_ptr, bool cart_mounted) override;
+ virtual void pcb_start(running_machine &machine, u8 *ciram_ptr, bool cart_mounted) override;
protected:
// device-level overrides
@@ -42,16 +42,16 @@ protected:
virtual void device_add_mconfig(machine_config &config) override;
private:
- // emulate the Game Genie!
+ // passthrough cart slot
required_device m_ggslot;
- int m_gg_bypass;
+ bool m_gg_bypass;
// GG codes
- uint16_t m_gg_addr[3];
- uint8_t m_gg_repl[3];
- uint8_t m_gg_comp[3];
- int m_gg_disable[3];
- int m_gg_is_comp[3];
+ u16 m_gg_addr[3];
+ u8 m_gg_repl[3];
+ u8 m_gg_comp[3];
+ bool m_gg_enable[3];
+ bool m_gg_is_comp[3];
};
diff --git a/src/devices/bus/nes/nes_slot.cpp b/src/devices/bus/nes/nes_slot.cpp
index 07f6c5b4f90..2274c2dba9f 100644
--- a/src/devices/bus/nes/nes_slot.cpp
+++ b/src/devices/bus/nes/nes_slot.cpp
@@ -478,12 +478,6 @@ DECLARE_WRITE_LINE_MEMBER(device_nes_cart_interface::set_irq_line)
m_maincpu->set_input_line(m6502_device::IRQ_LINE, state);
}
-void device_nes_cart_interface::reset_cpu()
-{
- // another hack
- m_maincpu->set_pc(0xfffc);
-}
-
//-------------------------------------------------
// Other helpers
//-------------------------------------------------
diff --git a/src/devices/bus/nes/nes_slot.h b/src/devices/bus/nes/nes_slot.h
index 63b562ecfee..a76c60e78ee 100644
--- a/src/devices/bus/nes/nes_slot.h
+++ b/src/devices/bus/nes/nes_slot.h
@@ -267,7 +267,6 @@ protected:
device_nes_cart_interface(const machine_config &mconfig, device_t &device);
DECLARE_WRITE_LINE_MEMBER(set_irq_line);
- void reset_cpu();
// internal state
uint8_t *m_prg;