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.
This commit is contained in:
0kmg 2022-04-02 12:47:01 -08:00 committed by GitHub
parent 8015fc79a7
commit 1039cc50df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 134 additions and 157 deletions

View File

@ -44224,11 +44224,7 @@ license:CC0
<feature name="pcb" value="CAMERICA-GAMEGENIE" />
<feature name="mirroring" value="horizontal" />
<dataarea name="prg" size="32768">
<rom name="genie v1.5" size="16384" crc="02b26e69" sha1="8bb0d98c4515cba77a805bc8b06788dd6ed6263c" offset="00000" /> <!-- an alt PCB with rom labeled v1.5a was dumped and the content was the same as this -->
<rom size="16384" offset="0x4000" loadflag="reload" />
</dataarea>
<dataarea name="chr" size="8192">
<rom name="0.chr" size="8192" crc="e698cc99" sha1="16a152a1e81328c8c66d9c5781d102b018bb6c36" offset="00000" />
<rom name="genie v1.5" size="4096" crc="1a3a22a1" sha1="aaa9833c3de2213f2b0326769b715651fd13fd84" offset="00000" /> <!-- an alt PCB with rom labeled v1.5a was dumped and the content was the same as this -->
</dataarea>
</part>
</software>

View File

@ -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);
}

View File

@ -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<nes_cart_slot_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];
};

View File

@ -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
//-------------------------------------------------

View File

@ -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;