From 90ce57831e624ea7107d6c6830eed132a8c84091 Mon Sep 17 00:00:00 2001 From: 0kmg <9137159+0kmg@users.noreply.github.com> Date: Sat, 19 Nov 2022 09:52:17 -0800 Subject: nintendo/nes_arcade_bl.cpp: Added emulation for NES side of hardware. (#10548) * bus/nes: Corrected pirate SMB3 IRQ counter. --- src/devices/bus/nes/bootleg.cpp | 2 +- src/mame/nintendo/nes_arcade_bl.cpp | 222 ++++++++++++++++++++++++++++++++---- 2 files changed, 201 insertions(+), 23 deletions(-) diff --git a/src/devices/bus/nes/bootleg.cpp b/src/devices/bus/nes/bootleg.cpp index c0ac3e1a35c..ade02369c9b 100644 --- a/src/devices/bus/nes/bootleg.cpp +++ b/src/devices/bus/nes/bootleg.cpp @@ -840,7 +840,7 @@ TIMER_CALLBACK_MEMBER(nes_smb3p_device::irq_timer_tick) // counter does not stop when interrupts are disabled if (m_irq_count != 0xffff) m_irq_count++; - else if (m_irq_enable) + if (m_irq_enable && m_irq_count == 0xffff) set_irq_line(ASSERT_LINE); } diff --git a/src/mame/nintendo/nes_arcade_bl.cpp b/src/mame/nintendo/nes_arcade_bl.cpp index 64e30a93859..371edf4d8f7 100644 --- a/src/mame/nintendo/nes_arcade_bl.cpp +++ b/src/mame/nintendo/nes_arcade_bl.cpp @@ -28,6 +28,16 @@ * 2nd half contains 8x 0x800 programs * 6 of them are identical * 2 others (0x4000 - 0x47ff and 0x6000 - 0x67ff) differ but are identical between themselves + + SMB3 bootleg ROMs appear to be hacks of the first Japanese release. CHR data + is identical. PRG data differs by only 46 bytes from the mapper 106 bootleg, + which similarly uses TTL in place of an MMC3. The mapper 106 bootleg adds a + Chinese title screen and gives the player 20 lives, suggesting it is a hack + of this original bootleg? + + TODO: + - machine is NOT WORKING since Z80 side isn't implemented + - coins, DIPs, etc */ @@ -50,24 +60,155 @@ public: nes_arcade_bl_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag), m_maincpu(*this, "maincpu"), - m_ppu(*this, "ppu") + m_ppu(*this, "ppu"), + m_in(*this, "IN%u", 0U), + m_nt_page(*this, "nt_page%u", 0U), + m_prg_bank(*this, "prg%u", 0U), + m_chr_bank(*this, "chr%u", 0U) { } void smb3bl(machine_config &config); +protected: + virtual void machine_start() override; + virtual void machine_reset() override; + private: required_device m_maincpu; required_device m_ppu; - + required_ioport_array<2> m_in; + required_memory_bank_array<4> m_nt_page; + required_memory_bank_array<4> m_prg_bank; + required_memory_bank_array<8> m_chr_bank; + + std::unique_ptr m_nt_ram; + u8 m_prg_chunks; + u8 m_input_latch[2]; + u8 m_input_strobe; + u16 m_irq_count; + bool m_irq_enable; + emu_timer* m_irq_timer; + + TIMER_CALLBACK_MEMBER(irq_timer_tick); + template u8 in_r(); + void in0_w(u8 data); + void set_mirroring(int mirroring); + void reg_w(offs_t offset, u8 data); void nes_cpu_map(address_map &map); + void nes_ppu_map(address_map &map); void timer_prg_map(address_map &map); void timer_io_map(address_map &map); }; +template +u8 nes_arcade_bl_state::in_r() +{ + if (m_input_strobe) + m_input_latch[Which] = m_in[Which]->read(); + + u8 ret = 0x40; // open bus - fake it til ya make it + + ret |= m_input_latch[Which] & 1; + m_input_latch[Which] >>= 1; + + return ret; +} + +void nes_arcade_bl_state::in0_w(u8 data) +{ + if (m_input_strobe & ~data & 1) + for (int i = 0; i < 2; i++) + m_input_latch[i] = m_in[i]->read(); + + m_input_strobe = data & 1; +} + +void nes_arcade_bl_state::set_mirroring(int mirroring) +{ + int bit = mirroring == PPU_MIRROR_HORZ; + + for (int i = 0; i < 4; i++) + m_nt_page[i]->set_entry(BIT(i, bit)); +} + +void nes_arcade_bl_state::reg_w(offs_t offset, u8 data) +{ + switch (offset) + { + case 0x00: case 0x01: case 0x02: case 0x03: + m_chr_bank[offset & 0x07]->set_entry((data & 0x7e) | (offset & 1)); + break; + case 0x04: case 0x05: case 0x06: case 0x07: + m_chr_bank[offset & 0x07]->set_entry(data & 0x7f); + break; + case 0x08: + case 0x0b: + m_prg_bank[offset & 0x03]->set_entry((data & 0x0f) | 0x10); + break; + case 0x09: + case 0x0a: + m_prg_bank[offset & 0x03]->set_entry(data & 0x1f); + break; + case 0x0c: + set_mirroring(data & 1 ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); + break; + case 0x0d: + m_irq_count = 0; + m_irq_enable = false; + m_maincpu->set_input_line(m6502_device::IRQ_LINE, CLEAR_LINE); + break; + case 0x0e: + m_irq_count = (m_irq_count & 0xff00) | data; + break; + case 0x0f: + m_irq_count = (m_irq_count & 0x00ff) | (data << 8); + m_irq_enable = true; + break; + } +} + +TIMER_CALLBACK_MEMBER(nes_arcade_bl_state::irq_timer_tick) +{ + // counter does not stop when interrupts are disabled + if (m_irq_count != 0xffff) + m_irq_count++; + if (m_irq_enable && m_irq_count == 0xffff) + m_maincpu->set_input_line(m6502_device::IRQ_LINE, ASSERT_LINE); +} + + void nes_arcade_bl_state::nes_cpu_map(address_map &map) { - map(0x8000, 0xffff).rom().region("maincpu", 0); + map(0x0000, 0x07ff).mirror(0x1800).ram(); + map(0x2000, 0x3fff).rw(m_ppu, FUNC(ppu2c0x_device::read), FUNC(ppu2c0x_device::write)); + map(0x4014, 0x4014).lw8(NAME([this] (address_space &space, u8 data) { m_ppu->spriteram_dma(space, data); })); + map(0x4016, 0x4016).rw(FUNC(nes_arcade_bl_state::in_r<0>), FUNC(nes_arcade_bl_state::in0_w)); // IN0 - input port 1 + map(0x4017, 0x4017).r(FUNC(nes_arcade_bl_state::in_r<1>)); // IN1 - input port 2 / PSG second control register + + map(0x6000, 0x7fff).ram(); + map(0x8000, 0x800f).mirror(0x7ff0).w(FUNC(nes_arcade_bl_state::reg_w)); + map(0x8000, 0x9fff).bankr(m_prg_bank[0]); + map(0xa000, 0xbfff).bankr(m_prg_bank[1]); + map(0xc000, 0xdfff).bankr(m_prg_bank[2]); + map(0xe000, 0xffff).bankr(m_prg_bank[3]); +} + +void nes_arcade_bl_state::nes_ppu_map(address_map &map) +{ + map(0x0000, 0x03ff).bankr(m_chr_bank[0]); + map(0x0400, 0x07ff).bankr(m_chr_bank[1]); + map(0x0800, 0x0bff).bankr(m_chr_bank[2]); + map(0x0c00, 0x0fff).bankr(m_chr_bank[3]); + map(0x1000, 0x13ff).bankr(m_chr_bank[4]); + map(0x1400, 0x17ff).bankr(m_chr_bank[5]); + map(0x1800, 0x1bff).bankr(m_chr_bank[6]); + map(0x1c00, 0x1fff).bankr(m_chr_bank[7]); + map(0x2000, 0x23ff).mirror(0x1000).bankrw(m_nt_page[0]); + map(0x2400, 0x27ff).mirror(0x1000).bankrw(m_nt_page[1]); + map(0x2800, 0x2bff).mirror(0x1000).bankrw(m_nt_page[2]); + map(0x2c00, 0x2fff).mirror(0x1000).bankrw(m_nt_page[3]); + map(0x3f00, 0x3fff).rw(m_ppu, FUNC(ppu2c0x_device::palette_read), FUNC(ppu2c0x_device::palette_write)); } void nes_arcade_bl_state::timer_prg_map(address_map &map) @@ -83,24 +224,24 @@ void nes_arcade_bl_state::timer_io_map(address_map &map) static INPUT_PORTS_START( smb3bl ) PORT_START("IN0") - PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN ) + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(1) + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(1) + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(1) // Select + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START1 ) + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(1) + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_START("IN1") - PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN ) - PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN ) + PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) + PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) + PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) // Select + PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_START2 ) + PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_PLAYER(2) + PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) + PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) + PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_START("DSW") PORT_DIPUNKNOWN_DIPLOC(0x01, 0x01, "SW1:1") @@ -114,6 +255,42 @@ static INPUT_PORTS_START( smb3bl ) INPUT_PORTS_END +void nes_arcade_bl_state::machine_start() +{ + m_nt_ram = std::make_unique(0x800); + for (auto &page : m_nt_page) + page->configure_entries(0, 2, m_nt_ram.get(), 0x400); + + m_prg_chunks = memregion("maincpu")->bytes() / 0x2000; + u8 *base = memregion("maincpu")->base(); + for (auto &bank : m_prg_bank) + bank->configure_entries(0, m_prg_chunks, base, 0x2000); + + int chr_chunks = memregion("gfx")->bytes() / 0x400; + base = memregion("gfx")->base(); + for (auto &bank : m_chr_bank) + bank->configure_entries(0, chr_chunks, base, 0x400); + + m_irq_timer = timer_alloc(FUNC(nes_arcade_bl_state::irq_timer_tick), this); + m_irq_timer->adjust(attotime::zero, 0, m_maincpu->clocks_to_attotime(1)); +} + +void nes_arcade_bl_state::machine_reset() +{ + for (int i = 0; i < 4; i++) + m_prg_bank[i]->set_entry(m_prg_chunks - 4 + i); // last 32K + + for (int i = 0; i < 8; i++) + m_chr_bank[i]->set_entry(i); + + set_mirroring(PPU_MIRROR_VERT); + m_input_latch[0] = 0; + m_input_latch[1] = 0; + m_input_strobe = 0; + m_irq_count = 0; + m_irq_enable = false; +} + void nes_arcade_bl_state::smb3bl(machine_config &config) { RP2A03G(config, m_maincpu, 3.579545_MHz_XTAL / 2); // TODO: verify divider, really RP2A03E @@ -127,10 +304,11 @@ void nes_arcade_bl_state::smb3bl(machine_config &config) screen.set_refresh_hz(60); screen.set_size(32*8, 262); screen.set_visarea(0*8, 32*8-1, 0*8, 30*8-1); - screen.set_screen_update("ppu", FUNC(ppu2c0x_device::screen_update)); + screen.set_screen_update(m_ppu, FUNC(ppu2c0x_device::screen_update)); PPU_2C02(config, m_ppu); - m_ppu->set_cpu_tag("maincpu"); + m_ppu->set_addrmap(0, &nes_arcade_bl_state::nes_ppu_map); + m_ppu->set_cpu_tag(m_maincpu); m_ppu->int_callback().set_inputline(m_maincpu, INPUT_LINE_NMI); SPEAKER(config, "mono").front_center(); @@ -157,4 +335,4 @@ ROM_END } // anonymous namespace -GAME( 1987, smb3bl, 0, smb3bl, smb3bl, nes_arcade_bl_state, empty_init, ROT0, "Sang Ho Soft", "Super Mario Bros. 3 (NES bootleg)", MACHINE_IS_SKELETON ) // 1987.10.01 in Z80 ROM +GAME( 1987, smb3bl, 0, smb3bl, smb3bl, nes_arcade_bl_state, empty_init, ROT0, "Sang Ho Soft", "Super Mario Bros. 3 (NES bootleg)", MACHINE_NOT_WORKING ) // 1987.10.01 in Z80 ROM -- cgit v1.2.3