// license:BSD-3-Clause
// copyright-holders:David Haywood
/*
NES clones that don't fit anywhere else / Plug & Play (non-VT)
*/
#include "emu.h"
#include "cpu/m6502/n2a03.h"
#include "video/ppu2c0x.h"
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "machine/bankdev.h"
class nes_clone_state : public driver_device
{
public:
nes_clone_state(const machine_config &mconfig, device_type type, const char *tag) :
driver_device(mconfig, type, tag),
m_maincpu(*this, "maincpu"),
m_screen(*this, "screen"),
m_io0(*this, "IO0"),
m_io1(*this, "IO1"),
m_ppu(*this, "ppu")
{ }
void nes_clone(machine_config &config);
void nes_clone_pal(machine_config &config);
void init_nes_clone();
protected:
virtual void machine_start() override;
virtual void machine_reset() override;
virtual void video_start() override;
void sprite_dma_w(address_space &space, uint8_t data);
virtual uint8_t in0_r();
virtual uint8_t in1_r();
virtual void in0_w(uint8_t data);
void nes_clone_basemap(address_map &map);
uint8_t* m_mainrom;
int m_mainromsize;
required_device<n2a03_device> m_maincpu;
required_device<screen_device> m_screen;
optional_ioport m_io0;
optional_ioport m_io1;
uint8_t m_latch0;
uint8_t m_latch1;
required_device<ppu2c0x_device> m_ppu;
private:
void nes_clone_map(address_map &map);
};
class nes_clone_dnce2000_state : public nes_clone_state
{
public:
nes_clone_dnce2000_state(const machine_config& mconfig, device_type type, const char* tag) :
nes_clone_state(mconfig, type, tag)
{ }
void nes_clone_dnce2000(machine_config& config);
private:
void nes_clone_dnce2000_map(address_map& map);
virtual void machine_start() override;
virtual void machine_reset() override;
uint8_t rom_r(offs_t offset);
void bank_w(uint8_t data);
int m_rombase;
};
class nes_clone_vtvppong_state : public nes_clone_state
{
public:
nes_clone_vtvppong_state(const machine_config& mconfig, device_type type, const char* tag) :
nes_clone_state(mconfig, type, tag)
{ }
void nes_clone_vtvppong(machine_config& config);
void init_vtvppong();
private:
void nes_clone_vtvppong_map(address_map& map);
};
class nes_clone_suduko_state : public nes_clone_state
{
public:
nes_clone_suduko_state(const machine_config& mconfig, device_type type, const char* tag) :
nes_clone_state(mconfig, type, tag)
{ }
void init_sudoku();
void nes_clone_suduko(machine_config& config);
private:
void nes_clone_suduko_map(address_map& map);
virtual void machine_start() override;
virtual void machine_reset() override;
uint8_t rom_r(offs_t offset);
void bank_w(uint8_t data);
int m_rombase;
};
class nes_clone_afbm7800_state : public nes_clone_state
{
public:
nes_clone_afbm7800_state(const machine_config& mconfig, device_type type, const char* tag) :
nes_clone_state(mconfig, type, tag),
m_prgbank(*this, "prgbank%u", 0),
m_cbank(*this, "cbank%u", 0),
m_nametables(*this, "nametable%u", 0),
m_charbank(*this, "charbank"),
m_ntbank(*this, "ntbank"),
m_rom_solderpad_bank(*this, "rom_sldpad_bank")
{ }
void nes_clone_afbm7800(machine_config& config);
protected:
virtual void machine_start() override;
virtual void machine_reset() override;
private:
// configured at startup
uint8_t m_maxchrbank;
void nes_clone_afbm7800_map(address_map &map);
void mapper_6000_w(uint8_t data);
void mapper_6001_w(uint8_t data);
void mapper_6002_w(uint8_t data);
void mapper_6003_w(uint8_t data);
void mapper_8000_w(uint8_t data);
void mapper_8001_w(uint8_t data);
void mapper_a000_w(uint8_t data);
void mapper_a001_w(uint8_t data);
void mapper_c000_w(uint8_t data);
void mapper_c001_w(uint8_t data);
void mapper_e000_w(uint8_t data);
void mapper_e001_w(uint8_t data);
uint8_t solderpad_r(offs_t offset);
uint8_t vram_r(offs_t offset);
void vram_w(offs_t offset, uint8_t data);
uint8_t m_banksel;
uint8_t m_bankregs[8];
uint8_t m_extraregs[4];
void update_banks();
void multigam3_mmc3_scanline_cb(int scanline, int vblank, int blanked);
int16_t m_multigam3_mmc3_scanline_counter;
uint8_t m_multigam3_mmc3_scanline_latch;
uint8_t m_mmc3_irq_enable;
std::vector<u8> m_vram;
uint8_t m_ntmirror;
uint8_t m_ramprot;
void update_nt_mirroring();
std::vector<u8> m_nt_ram;
void multigam_nt_w(offs_t offset, uint8_t data);
uint8_t multigam_nt_r(offs_t offset);
void vram_map(address_map &map);
void ntram_map(address_map &map);
void romarea_map(address_map &map);
required_memory_bank_array<4> m_prgbank;
required_memory_bank_array<6> m_cbank;
required_memory_bank_array<4> m_nametables;
required_device<address_map_bank_device> m_charbank;
required_device<address_map_bank_device> m_ntbank;
required_device<address_map_bank_device> m_rom_solderpad_bank;
};
void nes_clone_state::sprite_dma_w(address_space &space, uint8_t data)
{
int source = (data & 7);
m_ppu->spriteram_dma(space, source);
}
// Standard NES style inputs (not using bus device as there are no real NES controller ports etc. these are all-in-one units and can be custom
uint8_t nes_clone_state::in0_r()
{
//logerror("%s: in0_r\n", machine().describe_context());
uint8_t ret = 0x40;
ret |= m_latch0 & 1;
m_latch0 >>= 1;
return ret;
}
uint8_t nes_clone_state::in1_r()
{
//logerror("%s: in1_r\n", machine().describe_context());
uint8_t ret = 0x40;
ret |= m_latch1 & 1;
m_latch1 >>= 1;
return ret;
}
void nes_clone_state::in0_w(uint8_t data)
{
//logerror("%s: in0_w %02x\n", machine().describe_context(), data);
if (data & 0x01)
return;
m_latch0 = m_io0->read();
m_latch1 = m_io1->read();
}
void nes_clone_state::nes_clone_basemap(address_map& map)
{
map(0x0000, 0x07ff).ram();
map(0x2000, 0x3fff).rw(m_ppu, FUNC(ppu2c0x_device::read), FUNC(ppu2c0x_device::write));
map(0x4016, 0x4016).rw(FUNC(nes_clone_state::in0_r), FUNC(nes_clone_state::in0_w));
map(0x4017, 0x4017).r(FUNC(nes_clone_state::in1_r));
map(0x4014, 0x4014).w(FUNC(nes_clone_state::sprite_dma_w));
}
void nes_clone_state::nes_clone_map(address_map& map)
{
nes_clone_basemap(map);
map(0x8000, 0xffff).rom();
}
static INPUT_PORTS_START( nes_clone )
PORT_START("IO0")
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("A")
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("B")
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1)
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1)
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_8WAY
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_8WAY
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_8WAY
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_8WAY
PORT_START("IO1")
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_NAME("A")
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_NAME("B")
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(2)
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(2)
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_8WAY
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_8WAY
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_8WAY
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_8WAY
INPUT_PORTS_END
static INPUT_PORTS_START( papsudok )
PORT_START("IO0")
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_NAME("Auto Play")
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(1) PORT_NAME("7") PORT_CODE(KEYCODE_7_PAD)
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Exit / Menu")
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Start / Enter") // this is actually 2 buttons that map to the same input
PORT_START("IO1")
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(1) PORT_NAME("1") PORT_CODE(KEYCODE_1_PAD)
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(1) PORT_NAME("2") PORT_CODE(KEYCODE_2_PAD)
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_PLAYER(1) PORT_NAME("6") PORT_CODE(KEYCODE_6_PAD)
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_PLAYER(1) PORT_NAME("3") PORT_CODE(KEYCODE_3_PAD)
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON9 ) PORT_PLAYER(1) PORT_NAME("9") PORT_CODE(KEYCODE_9_PAD)
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON10 ) PORT_PLAYER(1) PORT_NAME("5") PORT_CODE(KEYCODE_5_PAD)
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_BUTTON11 ) PORT_PLAYER(1) PORT_NAME("8") PORT_CODE(KEYCODE_8_PAD)
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_BUTTON12 ) PORT_PLAYER(1) PORT_NAME("4") PORT_CODE(KEYCODE_4_PAD)
INPUT_PORTS_END
static INPUT_PORTS_START( dnce2000 )
PORT_START("IO0")
PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_NAME("Down-Left")
PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_NAME("Down-Right")
PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_SELECT ) PORT_PLAYER(1) PORT_NAME("Up-Left / Select")
PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START ) PORT_PLAYER(1) PORT_NAME("Up-Right / Start")
PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_16WAY // NOT A JOYSTICK!!
PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_16WAY
PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_16WAY
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_16WAY
PORT_START("IO1")
PORT_BIT( 0xff, IP_ACTIVE_HIGH, IPT_UNUSED )
INPUT_PORTS_END
void nes_clone_state::video_start()
{
}
void nes_clone_state::machine_reset()
{
m_latch0 = 0;
m_latch1 = 0;
}
void nes_clone_state::machine_start()
{
m_mainrom = memregion("maincpu")->base();
m_mainromsize = memregion("maincpu")->bytes();
save_item(NAME(m_latch0));
save_item(NAME(m_latch1));
}
void nes_clone_state::nes_clone(machine_config &config)
{
/* basic machine hardware */
N2A03(config, m_maincpu, NTSC_APU_CLOCK);
m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_state::nes_clone_map);
/* video hardware */
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
m_screen->set_refresh_hz(60.0988);
m_screen->set_vblank_time(ATTOSECONDS_IN_USEC((113.66/(NTSC_APU_CLOCK.dvalue()/1000000)) * (ppu2c0x_device::VBLANK_LAST_SCANLINE_NTSC-ppu2c0x_device::VBLANK_FIRST_SCANLINE+1+2)));
m_screen->set_size(32*8, 262);
m_screen->set_visarea(0*8, 32*8-1, 0*8, 30*8-1);
m_screen->set_screen_update(m_ppu, FUNC(ppu2c0x_device::screen_update));
PPU_2C02(config, m_ppu);
m_ppu->set_cpu_tag("maincpu");
m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
/* sound hardware */
SPEAKER(config, "mono").front_center();
m_maincpu->add_route(ALL_OUTPUTS, "mono", 0.50);
}
void nes_clone_state::nes_clone_pal(machine_config &config)
{
/* basic machine hardware */
N2A03(config, m_maincpu, PALC_APU_CLOCK);
m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_state::nes_clone_map);
/* video hardware */
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
m_screen->set_refresh_hz(50.0070);
m_screen->set_vblank_time(ATTOSECONDS_IN_USEC((113.66 / (PALC_APU_CLOCK.dvalue() / 1000000)) * (ppu2c0x_device::VBLANK_LAST_SCANLINE_PAL - ppu2c0x_device::VBLANK_FIRST_SCANLINE_PALC + 1 + 2)));
m_screen->set_size(32 * 8, 312);
m_screen->set_visarea(0 * 8, 32 * 8 - 1, 0 * 8, 30 * 8 - 1);
m_screen->set_screen_update(m_ppu, FUNC(ppu2c0x_device::screen_update));
PPU_PALC(config, m_ppu);
m_ppu->set_cpu_tag("maincpu");
m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI);
/* sound hardware */
SPEAKER(config, "mono").front_center();
m_maincpu->add_route(ALL_OUTPUTS, "mono", 0.50);
}
/**************************************************
Dance 2000 Specifics
**************************************************/
void nes_clone_dnce2000_state::nes_clone_dnce2000(machine_config& config)
{
nes_clone_pal(config);
m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_dnce2000_state::nes_clone_dnce2000_map);
}
void nes_clone_dnce2000_state::nes_clone_dnce2000_map(address_map& map)
{
nes_clone_basemap(map);
map(0x8000, 0xffff).rw(FUNC(nes_clone_dnce2000_state::rom_r), FUNC(nes_clone_dnce2000_state::bank_w));
}
void nes_clone_dnce2000_state::machine_reset()
{
nes_clone_state::machine_reset();
m_rombase = 0;
}
void nes_clone_dnce2000_state::machine_start()
{
nes_clone_state::machine_start();
save_item(NAME(m_rombase));
}
uint8_t nes_clone_dnce2000_state::rom_r(offs_t offset)
{
return m_mainrom[(offset + (m_rombase * 0x8000)) & (m_mainromsize - 1)];
}
void nes_clone_dnce2000_state::bank_w(uint8_t data)
{
m_rombase = data;
}
/**************************************************
Virtual Ping Pong Specifics
**************************************************/
void nes_clone_vtvppong_state::nes_clone_vtvppong(machine_config& config)
{
nes_clone_pal(config);
m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_vtvppong_state::nes_clone_vtvppong_map);
}
void nes_clone_vtvppong_state::nes_clone_vtvppong_map(address_map& map)
{
nes_clone_basemap(map);
map(0x8000, 0xffff).rom().region("maincpu", 0x38000);
}
/**************************************************
Atari Flashback Specifics
**************************************************/
void nes_clone_afbm7800_state::nes_clone_afbm7800(machine_config& config)
{
nes_clone_pal(config);
m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_afbm7800_state::nes_clone_afbm7800_map);
ADDRESS_MAP_BANK(config, "charbank").set_map(&nes_clone_afbm7800_state::vram_map).set_options(ENDIANNESS_NATIVE, 8, 14, 0x2000);
ADDRESS_MAP_BANK(config, "ntbank").set_map(&nes_clone_afbm7800_state::ntram_map).set_options(ENDIANNESS_NATIVE, 8, 12, 0x1000);
ADDRESS_MAP_BANK(config, "rom_sldpad_bank").set_map(&nes_clone_afbm7800_state::romarea_map).set_options(ENDIANNESS_NATIVE, 8, 16, 0x8000);
}
void nes_clone_afbm7800_state::update_banks()
{
uint8_t innerbankmask;
uint8_t outerbank;
uint8_t selected_banks[4] = { 0x00, 0x00, 0x00, 0x00 };
if (m_extraregs[1] & 1) // ROM or solder pad select
{
m_rom_solderpad_bank->set_bank(1);
}
else
{
m_rom_solderpad_bank->set_bank(0);
}
if (m_extraregs[0] & 0x40)
{
innerbankmask = 0x0f;
outerbank = (m_extraregs[0] & 0x07) << 4;
}
else
{
innerbankmask = 0x1f;
outerbank = (m_extraregs[0] & 0x06) << 4;
}
if ((m_extraregs[3] & 0x03) == 0x00) // MMC3 style mode
{
selected_banks[1] = outerbank | (m_bankregs[7] & innerbankmask);
selected_banks[3] = outerbank | (0x1f & innerbankmask);
if (m_banksel & 0x40)
{
selected_banks[0] = outerbank | (0x1e & innerbankmask);
selected_banks[2] = outerbank | (m_bankregs[6] & innerbankmask);
}
else
{
selected_banks[0] = outerbank | (m_bankregs[6] & innerbankmask);
selected_banks[2] = outerbank | (0x1e & innerbankmask);
}
}
else if ((m_extraregs[3] & 0x03) == 0x03)
{
int basebank = (m_bankregs[6] & innerbankmask);
selected_banks[0] = outerbank | (basebank + 0);
selected_banks[1] = outerbank | (basebank + 1);
selected_banks[2] = outerbank | (basebank + 2);
selected_banks[3] = outerbank | (basebank + 3);
}
else // 01 and 02 cases
{
int basebank = (m_bankregs[6] & innerbankmask);
selected_banks[0] = outerbank | (basebank + 0);
selected_banks[1] = outerbank | (basebank + 1);
selected_banks[2] = outerbank | (basebank + 0);
selected_banks[3] = outerbank | (basebank + 1);
}
m_prgbank[0]->set_entry(selected_banks[0]);
m_prgbank[1]->set_entry(selected_banks[1]);
m_prgbank[2]->set_entry(selected_banks[2]);
m_prgbank[3]->set_entry(selected_banks[3]);
// chrbank stuff
uint8_t selected_chrbanks[6] = { 0x00, 0x02, 0x04, 0x05, 0x06, 0x07 };
if (m_extraregs[3] & 0x10)
{
int outerchrbank = m_extraregs[2] & 0xf;
m_charbank->set_bank(0);
// should also have m_extraregs[0] & 0x38) applied?
selected_chrbanks[0] = (outerchrbank << 3) | 0x0;
selected_chrbanks[1] = (outerchrbank << 3) | 0x2;
selected_chrbanks[2] = (outerchrbank << 3) | 0x4;
selected_chrbanks[3] = (outerchrbank << 3) | 0x5;
selected_chrbanks[4] = (outerchrbank << 3) | 0x6;
selected_chrbanks[5] = (outerchrbank << 3) | 0x7;
}
else // MMC3 mode
{
int bankmask;
int outerchrbank;
/* not correct? desert falcon
if (m_extraregs[0] & 0x80)
{
bankmask = 0x0f;
outerchrbank = (m_extraregs[0] & 0x38) << 1;
}
else
*/
{
bankmask = 0x1f;
outerchrbank = (m_extraregs[0] & 0x30) << 1;
}
if (m_banksel & 0x80)
m_charbank->set_bank(1);
else
m_charbank->set_bank(0);
selected_chrbanks[0] = (outerchrbank | (m_bankregs[0] & bankmask));
selected_chrbanks[1] = (outerchrbank | (m_bankregs[1] & bankmask));
selected_chrbanks[2] = (outerchrbank | (m_bankregs[2] & bankmask));
selected_chrbanks[3] = (outerchrbank | (m_bankregs[3] & bankmask));
selected_chrbanks[4] = (outerchrbank | (m_bankregs[4] & bankmask));
selected_chrbanks[5] = (outerchrbank | (m_bankregs[5] & bankmask));
}
// have to mask with m_maxchrbank because otherwise banks are set at 0x40 (would lower banks atually be chrrom?)
m_cbank[0]->set_entry((selected_chrbanks[0] & m_maxchrbank)>>1);
m_cbank[1]->set_entry((selected_chrbanks[1] & m_maxchrbank)>>1);
m_cbank[2]->set_entry((selected_chrbanks[2] & m_maxchrbank));
m_cbank[3]->set_entry((selected_chrbanks[3] & m_maxchrbank));
m_cbank[4]->set_entry((selected_chrbanks[4] & m_maxchrbank));
m_cbank[5]->set_entry((selected_chrbanks[5] & m_maxchrbank));
}
void nes_clone_afbm7800_state::update_nt_mirroring()
{
if (m_ntmirror & 1)
{
m_nametables[0]->set_entry(0);
m_nametables[1]->set_entry(0);
m_nametables[2]->set_entry(1);
m_nametables[3]->set_entry(1);
}
else
{
m_nametables[0]->set_entry(0);
m_nametables[1]->set_entry(1);
m_nametables[2]->set_entry(0);
m_nametables[3]->set_entry(1);
}
}
void nes_clone_afbm7800_state::mapper_8000_w(uint8_t data)
{
m_banksel = data;
update_banks();
}
void nes_clone_afbm7800_state::mapper_8001_w(uint8_t data)
{
m_bankregs[m_banksel & 0x7] = data;
update_banks();
}
void nes_clone_afbm7800_state::mapper_a000_w(uint8_t data)
{
// nametable mirroring
m_ntmirror = data;
update_nt_mirroring();
}
void nes_clone_afbm7800_state::mapper_a001_w(uint8_t data)
{
// ram protect
m_ramprot = data;
}
void nes_clone_afbm7800_state::mapper_c000_w(uint8_t data)
{
// irq latch
m_multigam3_mmc3_scanline_counter = data ^ 0xff;
}
void nes_clone_afbm7800_state::mapper_c001_w(uint8_t data)
{
// irq reload
m_multigam3_mmc3_scanline_latch = data;
}
void nes_clone_afbm7800_state::mapper_e000_w(uint8_t data)
{
// irq disable
m_maincpu->set_input_line(M6502_IRQ_LINE, CLEAR_LINE);
m_mmc3_irq_enable = 0;
}
void nes_clone_afbm7800_state::mapper_e001_w(uint8_t data)
{
// irq enable
m_mmc3_irq_enable = 1;
}
void nes_clone_afbm7800_state::multigam3_mmc3_scanline_cb( int scanline, int vblank, int blanked )
{
if (m_mmc3_irq_enable)
{
if (!vblank && !blanked)
{
if (--m_multigam3_mmc3_scanline_counter == -1)
{
m_multigam3_mmc3_scanline_counter = m_multigam3_mmc3_scanline_latch;
m_maincpu->set_input_line(M6502_IRQ_LINE, ASSERT_LINE);
}
}
}
}
void nes_clone_afbm7800_state::mapper_6000_w(uint8_t data)
{
if (m_ramprot & 0x80)
{
if (!(m_extraregs[3] & 0x80))
{
m_extraregs[0] = data;
update_banks();
}
}
}
void nes_clone_afbm7800_state::mapper_6001_w(uint8_t data)
{
if (m_ramprot & 0x80)
{
m_extraregs[1] = data;
update_banks();
}
}
void nes_clone_afbm7800_state::mapper_6002_w(uint8_t data)
{
if (m_ramprot & 0x80)
{
m_extraregs[2] = data;
update_banks();
}
}
void nes_clone_afbm7800_state::mapper_6003_w(uint8_t data)
{
if (m_ramprot & 0x80)
{
if (!(m_extraregs[3] & 0x80))
{
m_extraregs[3] = data;
update_banks();
}
}
}
void nes_clone_afbm7800_state::nes_clone_afbm7800_map(address_map &map)
{
nes_clone_basemap(map);
map(0x6000, 0x6000).mirror(0x1ffc).w(FUNC(nes_clone_afbm7800_state::mapper_6000_w));
map(0x6001, 0x6001).mirror(0x1ffc).w(FUNC(nes_clone_afbm7800_state::mapper_6001_w));
map(0x6002, 0x6002).mirror(0x1ffc).w(FUNC(nes_clone_afbm7800_state::mapper_6002_w));
map(0x6003, 0x6003).mirror(0x1ffc).w(FUNC(nes_clone_afbm7800_state::mapper_6003_w));
map(0x8000, 0xffff).m(m_rom_solderpad_bank, FUNC(address_map_bank_device::amap8));
map(0x8000, 0x8000).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_8000_w));
map(0x8001, 0x8001).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_8001_w));
map(0xa000, 0xa000).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_a000_w));
map(0xa001, 0xa001).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_a001_w));
map(0xc000, 0xc000).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_c000_w));
map(0xc001, 0xc001).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_c001_w));
map(0xe000, 0xe000).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_e000_w));
map(0xe001, 0xe001).mirror(0x1ffe).w(FUNC(nes_clone_afbm7800_state::mapper_e001_w));
}
uint8_t nes_clone_afbm7800_state::solderpad_r(offs_t offset)
{
logerror("%s: solderpad read\n", machine().describe_context());
return 0x00;
}
void nes_clone_afbm7800_state::romarea_map(address_map &map)
{
// when (m_extraregs[1] & 1) == 1
map(0x0000, 0x1fff).bankr("prgbank0");
map(0x2000, 0x3fff).bankr("prgbank1");
map(0x4000, 0x5fff).bankr("prgbank2");
map(0x6000, 0x7fff).bankr("prgbank3");
// when (m_extraregs[1] & 1) == 1
map(0x8000, 0xffff).r(FUNC(nes_clone_afbm7800_state::solderpad_r));
}
void nes_clone_afbm7800_state::vram_map(address_map &map)
{
// in MMC3 mode when (m_banksel & 0x80) == 0x00 or when not in MMC3 mode
map(0x0000, 0x07ff).bankrw("cbank0");
map(0x0800, 0x0fff).bankrw("cbank1");
map(0x1000, 0x13ff).bankrw("cbank2");
map(0x1400, 0x17ff).bankrw("cbank3");
map(0x1800, 0x1bff).bankrw("cbank4");
map(0x1c00, 0x1fff).bankrw("cbank5");
// in MMC3 mode when (m_banksel & 0x80) == 0x80
map(0x2000, 0x23ff).bankrw("cbank2");
map(0x2400, 0x27ff).bankrw("cbank3");
map(0x2800, 0x2bff).bankrw("cbank4");
map(0x2c00, 0x2fff).bankrw("cbank5");
map(0x3000, 0x37ff).bankrw("cbank0");
map(0x3800, 0x3fff).bankrw("cbank1");
}
void nes_clone_afbm7800_state::ntram_map(address_map& map)
{
map(0x0000, 0x03ff).bankrw("nametable0");
map(0x0400, 0x07ff).bankrw("nametable1");
map(0x0800, 0x0bff).bankrw("nametable2");
map(0x0c00, 0x0fff).bankrw("nametable3");
}
void nes_clone_afbm7800_state::machine_reset()
{
nes_clone_state::machine_reset();
m_banksel = 0;
m_bankregs[0] = 0x00;
m_bankregs[1] = 0x00;
m_bankregs[2] = 0x00;
m_bankregs[3] = 0x00;
m_bankregs[4] = 0x00;
m_bankregs[5] = 0x00;
m_bankregs[6] = 0x00;
m_bankregs[7] = 0x00;
m_extraregs[0] = 0x00;
m_extraregs[1] = 0x00;
m_extraregs[2] = 0x00;
m_extraregs[3] = 0x00;
m_ntmirror = 0;
m_ramprot = 0;
update_banks();
update_nt_mirroring();
m_multigam3_mmc3_scanline_counter = 0;
m_multigam3_mmc3_scanline_latch = 0;
m_mmc3_irq_enable = 0;
}
uint8_t nes_clone_afbm7800_state::vram_r(offs_t offset)
{
return m_charbank->read8(offset);
}
void nes_clone_afbm7800_state::vram_w(offs_t offset, uint8_t data)
{
m_charbank->write8(offset, data);
}
uint8_t nes_clone_afbm7800_state::multigam_nt_r(offs_t offset)
{
return m_ntbank->read8(offset);
}
void nes_clone_afbm7800_state::multigam_nt_w(offs_t offset, uint8_t data)
{
m_ntbank->write8(offset, data);
}
void nes_clone_afbm7800_state::machine_start()
{
nes_clone_state::machine_start();
uint8_t* ROM = memregion("maincpu")->base();
for (int i = 0; i < 4; i++)
m_prgbank[i]->configure_entries(0, 0x100000 / 0x2000, &ROM[0x00000], 0x02000);
m_vram.resize(0x8000);
m_maxchrbank = (0x8000/0x400)-1;
for (int i = 0; i < 2; i++)
m_cbank[i]->configure_entries(0, 0x8000 / 0x800, &m_vram[0], 0x800);
for (int i = 2; i < 6; i++)
m_cbank[i]->configure_entries(0, 0x8000 / 0x400, &m_vram[0], 0x400);
m_nt_ram.resize(0x800);
for (int i = 0; i < 4; i++)
m_nametables[i]->configure_entries(0, 0x800 / 0x400, &m_nt_ram[0], 0x400);
m_ppu->space(AS_PROGRAM).install_readwrite_handler(0x0000, 0x1fff, read8sm_delegate(*this, FUNC(nes_clone_afbm7800_state::vram_r)), write8sm_delegate(*this, FUNC(nes_clone_afbm7800_state::vram_w)));
m_ppu->set_scanline_callback(*this, FUNC(nes_clone_afbm7800_state::multigam3_mmc3_scanline_cb));
m_ppu->space(AS_PROGRAM).install_readwrite_handler(0x2000, 0x3eff, read8sm_delegate(*this, FUNC(nes_clone_afbm7800_state::multigam_nt_r)), write8sm_delegate(*this, FUNC(nes_clone_afbm7800_state::multigam_nt_w)));
save_item(NAME(m_vram));
save_item(NAME(m_nt_ram));
save_item(NAME(m_multigam3_mmc3_scanline_counter));
save_item(NAME(m_multigam3_mmc3_scanline_latch));
save_item(NAME(m_mmc3_irq_enable));
save_item(NAME(m_ntmirror));
save_item(NAME(m_ramprot));
save_item(NAME(m_banksel));
save_item(NAME(m_bankregs));
save_item(NAME(m_extraregs));
}
/**************************************************
Sudoku Specifics
**************************************************/
void nes_clone_suduko_state::machine_reset()
{
nes_clone_state::machine_reset();
m_rombase = 0;
}
void nes_clone_suduko_state::machine_start()
{
nes_clone_state::machine_start();
save_item(NAME(m_rombase));
}
void nes_clone_suduko_state::nes_clone_suduko(machine_config& config)
{
nes_clone(config);
m_maincpu->set_addrmap(AS_PROGRAM, &nes_clone_suduko_state::nes_clone_suduko_map);
}
void nes_clone_suduko_state::nes_clone_suduko_map(address_map& map)
{
nes_clone_basemap(map);
map(0x8000, 0xffff).rw(FUNC(nes_clone_suduko_state::rom_r), FUNC(nes_clone_suduko_state::bank_w));
}
uint8_t nes_clone_suduko_state::rom_r(offs_t offset)
{
return m_mainrom[(offset + (m_rombase * 0x8000)) & (m_mainromsize - 1)];
}
void nes_clone_suduko_state::bank_w(uint8_t data)
{
logerror("%04x: bank_w %02x\n", machine().describe_context(), data);
m_rombase = data;
}
void nes_clone_suduko_state::init_sudoku()
{
u8 *src = memregion("maincpu")->base();
int len = memregion("maincpu")->bytes();
std::vector<u8> buffer(len);
{
for (int i = 0; i < len; i += 8)
{
buffer[i+0] = src[i+5];
buffer[i+1] = src[i+4];
buffer[i+2] = src[i+7];
buffer[i+3] = src[i+6];
buffer[i+4] = src[i+1];
buffer[i+5] = src[i+0];
buffer[i+6] = src[i+3];
buffer[i+7] = src[i+2];
}
std::copy(buffer.begin(), buffer.end(), &src[0]);
}
}
void nes_clone_vtvppong_state::init_vtvppong()
{
u8 *src = memregion("maincpu")->base();
int len = memregion("maincpu")->bytes();
std::vector<u8> buffer(len);
{
for (int i = 0; i < len; i++)
{
int newaddr = bitswap<18>(i, 17, 16, 15, 13, 14, 12,
11, 10, 9, 8,
7, 6, 5, 4,
3, 2, 1, 0);
buffer[i] = src[newaddr];
}
std::copy(buffer.begin(), buffer.end(), &src[0]);
}
#if 0
FILE *fp;
char filename[256];
sprintf(filename,"decrypted_%s", machine().system().name);
fp=fopen(filename, "w+b");
if (fp)
{
fwrite(&src[0], len, 1, fp);
fclose(fp);
}
#endif
}
ROM_START( papsudok )
ROM_REGION( 0x80000, "maincpu", 0 )
ROM_LOAD( "sudoku2.bin", 0x00000, 0x80000, CRC(d1ffcc1e) SHA1(2010e60933a08d0b9271ef37f338758aacba6d2d) )
ROM_END
ROM_START( pjoypj001 )
ROM_REGION( 0x100000, "maincpu", ROMREGION_ERASE00 )
ROM_LOAD( "powerjoy_pj001_lh28f008sc_89a6.bin", 0x00000, 0x100000, CRC(e655e0aa) SHA1(c96d3422e26451c366fee2151fedccb95014cbc7) )
ROM_REGION( 0x80000, "gfx1", ROMREGION_ERASE00 )
ROM_LOAD( "powerjoy_pj001_te28f400ceb_00894471.bin", 0x00000, 0x80000, CRC(edca9b66) SHA1(f2f6d9043f524748282065b2fa0ca323ddd7d008) )
ROM_END
ROM_START( afbm7800 )
ROM_REGION( 0x100000, "maincpu", 0 )
ROM_LOAD( "atariflashbackmini7800.bin", 0x00000, 0x100000, CRC(da4d9483) SHA1(c04465ff5bd5ca7abf088fe771b8e71c157afb89) )
ROM_END
ROM_START( dnce2000 ) // use Mapper 241 if you want to run this in a NES emulator
ROM_REGION( 0x40000, "maincpu", 0 )
ROM_LOAD( "dance.bin", 0x00000, 0x40000, CRC(0982bb50) SHA1(bd608159d7e624ea345f2a188de51cb1aa116421) )
ROM_END
ROM_START( vtvppong )
ROM_REGION( 0x40000, "maincpu", ROMREGION_ERASE00 ) // high bit is never set in the first 0x28000 bytes of this ROM, probably 7-bit sound data? code might need opcode bits swapping
ROM_LOAD( "vtvpongcpu.bin", 0x00000, 0x40000, CRC(52df95fa) SHA1(3015bcc90eee862b3568f122b402c9defa566aab) )
ROM_REGION( 0x20000, "gfx1", ROMREGION_ERASE00 )
ROM_LOAD( "vtvpongppu.bin", 0x00000, 0x20000, CRC(474dfc0c) SHA1(4d0afab111e40172ae0b31e94f1b74b73a18385f) )
ROM_END
void nes_clone_state::init_nes_clone()
{
}
// aka 'Atari Flashback' or 'Atari Flashback 1'
// "Flashback Mini 7800 uses normal NES-style hardware, together with a mapper chipset similar to the Waixing kk33xx cartridges (NES 2.0 Mapper 534)"
CONS( 2004, afbm7800, 0, 0, nes_clone_afbm7800, nes_clone, nes_clone_afbm7800_state, init_nes_clone, "Atari / Nice Code", "Atari Flashback Mini 7800", 0 )
CONS( 200?, dnce2000, 0, 0, nes_clone_dnce2000, dnce2000, nes_clone_dnce2000_state, init_nes_clone, "Shenzhen Soyin Electric Appliance Ind. Co., Ltd.", "Dance 2000 / Hot 2000 (Jin Bao TV Dancing Carpet, SY-2000-04)", 0 )
// Black pad marked 'SUDOKU' with tails on the S and U characters looping over the logo. Box says "Plug and Play Sudoku"
// Has 2 sets of 4 buttons in circular 'direction pad' layouts (on the left for directions, on the right for functions) and 9 red numbered buttons with red power LED on left of them, and reset button on right
// Alt. version was released with 'New York Times' titlescreen
CONS( 200?, papsudok, 0, 0, nes_clone_suduko, papsudok, nes_clone_suduko_state, init_sudoku, "Nice Code", "Plug and Play Sudoku Game (NES based)", 0 ) // plays, but unclear how 'save' feature is meant to work, is it meant to save after shutdown or not? no obvious writes
CONS( 200?, vtvppong, 0, 0, nes_clone_vtvppong, nes_clone, nes_clone_vtvppong_state, init_vtvppong, "<unknown>", "Virtual TV Ping Pong", MACHINE_NOT_WORKING )
CONS( 200?, pjoypj001, 0, 0, nes_clone, nes_clone, nes_clone_state, init_nes_clone, "Trump Grand", "PowerJoy (PJ001, NES based plug & play)", MACHINE_NOT_WORKING )