mirror of
https://github.com/holub/mame
synced 2025-04-20 15:32:45 +03:00
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:
parent
8015fc79a7
commit
1039cc50df
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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];
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
//-------------------------------------------------
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user