From 33f2b341982515204a0975ba44b1a9831f6eb9f2 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Sun, 18 Sep 2022 06:05:43 +1000 Subject: [PATCH] bus/gameboy: Added partial support for some Chinese multi-game cartridges. Cartridge RAM features are not implemented yet. New working software list items --------------------- gbcolor.xml: New Super Color 145 in 1 (China) [taizou, Vas Crabb] --- hash/gbcolor.xml | 40 +++-- scripts/src/bus.lua | 2 + src/devices/bus/gameboy/carts.cpp | 3 + src/devices/bus/gameboy/carts.h | 1 + src/devices/bus/gameboy/gbslot.cpp | 3 + src/devices/bus/gameboy/gbxfile.h | 1 + src/devices/bus/gameboy/mbc6.cpp | 52 +++--- src/devices/bus/gameboy/slmulti.cpp | 235 ++++++++++++++++++++++++++++ src/devices/bus/gameboy/slmulti.h | 18 +++ 9 files changed, 318 insertions(+), 37 deletions(-) create mode 100644 src/devices/bus/gameboy/slmulti.cpp create mode 100644 src/devices/bus/gameboy/slmulti.h diff --git a/hash/gbcolor.xml b/hash/gbcolor.xml index 783815943dd..0444997884e 100644 --- a/hash/gbcolor.xml +++ b/hash/gbcolor.xml @@ -26287,8 +26287,8 @@ license:CC0 - - + + @@ -26301,8 +26301,8 @@ license:CC0 - - + + @@ -26316,8 +26316,8 @@ license:CC0 - - + + @@ -26329,8 +26329,8 @@ license:CC0 - - + + @@ -26345,8 +26345,8 @@ license:CC0 - - + + @@ -26360,13 +26360,27 @@ license:CC0 - - + + + + + New Super Color 145 in 1 (China) + 200? + J.Y. Company + + + + + + + + + E Mo Dao (China, ripped from 8 in 1 multicart) 200? @@ -27601,7 +27615,7 @@ license:CC0 used here repeated twice. we need to redump the standalone cart to confirm the size but the 256KB version is definitely underdumped and misses sprite data --> - Super Mario 3 Special (China) + Super Mario 3 Special (China) 2000 Yong Yong diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index 077ae634d96..555a6c3597b 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -3719,6 +3719,8 @@ if (BUSES["GAMEBOY"]~=null) then MAME_DIR .. "src/devices/bus/gameboy/mmm01.h", MAME_DIR .. "src/devices/bus/gameboy/rom.cpp", MAME_DIR .. "src/devices/bus/gameboy/rom.h", + MAME_DIR .. "src/devices/bus/gameboy/slmulti.cpp", + MAME_DIR .. "src/devices/bus/gameboy/slmulti.h", MAME_DIR .. "src/devices/bus/gameboy/slot.cpp", MAME_DIR .. "src/devices/bus/gameboy/slot.h", MAME_DIR .. "src/devices/bus/gameboy/tama5.cpp", diff --git a/src/devices/bus/gameboy/carts.cpp b/src/devices/bus/gameboy/carts.cpp index e783d11c9c9..94ffa62cec9 100644 --- a/src/devices/bus/gameboy/carts.cpp +++ b/src/devices/bus/gameboy/carts.cpp @@ -18,6 +18,7 @@ #include "mbc7.h" #include "mmm01.h" #include "rom.h" +#include "slmulti.h" #include "tama5.h" @@ -53,6 +54,7 @@ char const *const GB_LICHENG = "rom_licheng"; char const *const GB_NEWGBCHK = "rom_newgbchk"; char const *const GB_VF001 = "rom_vf001"; char const *const GB_DIGIMON = "rom_digimon"; +char const *const GB_SLMULTI = "rom_slmulti"; char const *const MEGADUCK_STD = "rom"; char const *const MEGADUCK_BANKED = "rom_banked"; @@ -95,6 +97,7 @@ void gameboy_cartridges(device_slot_interface &device) device.option_add_internal(slotoptions::GB_NEWGBCHK, GB_ROM_NEWGBCHK); device.option_add_internal(slotoptions::GB_VF001, GB_ROM_VF001); device.option_add_internal(slotoptions::GB_DIGIMON, GB_ROM_DIGIMON); + device.option_add_internal(slotoptions::GB_SLMULTI, GB_ROM_SLMULTI); } diff --git a/src/devices/bus/gameboy/carts.h b/src/devices/bus/gameboy/carts.h index e4660129be9..47c8e8b7568 100644 --- a/src/devices/bus/gameboy/carts.h +++ b/src/devices/bus/gameboy/carts.h @@ -47,6 +47,7 @@ extern char const *const GB_LICHENG; extern char const *const GB_NEWGBCHK; extern char const *const GB_VF001; extern char const *const GB_DIGIMON; +extern char const *const GB_SLMULTI; extern char const *const MEGADUCK_STD; extern char const *const MEGADUCK_BANKED; diff --git a/src/devices/bus/gameboy/gbslot.cpp b/src/devices/bus/gameboy/gbslot.cpp index c81b307f06a..0e69f0cb972 100644 --- a/src/devices/bus/gameboy/gbslot.cpp +++ b/src/devices/bus/gameboy/gbslot.cpp @@ -743,6 +743,9 @@ std::optional probe_gbx_footer(std::string_view tag, util::random_ case gbxfile::TYPE_VF001: result = slotoptions::GB_VF001; break; + case gbxfile::TYPE_SLMULTI: + result = slotoptions::GB_SLMULTI; + break; } if (result) { diff --git a/src/devices/bus/gameboy/gbxfile.h b/src/devices/bus/gameboy/gbxfile.h index ca752f56cf6..5104afe892a 100644 --- a/src/devices/bus/gameboy/gbxfile.h +++ b/src/devices/bus/gameboy/gbxfile.h @@ -53,6 +53,7 @@ enum : u32 TYPE_CAMERA = 0x43414d52, // 'CAMR' TYPE_HUC1 = 0x48554331, // 'HUC1' TYPE_HUC3 = 0x48554333, // 'HUC3' + TYPE_SLMULTI = 0x4c424d43, // 'LBMC' TYPE_LICHENG = 0x4c494348, // 'LICH' TYPE_M161 = 0x4d313631, // 'M161' TYPE_MBC1_COLL = 0x4d42314d, // 'MB1M' diff --git a/src/devices/bus/gameboy/mbc6.cpp b/src/devices/bus/gameboy/mbc6.cpp index e8072bf10f8..eee9867c865 100644 --- a/src/devices/bus/gameboy/mbc6.cpp +++ b/src/devices/bus/gameboy/mbc6.cpp @@ -103,7 +103,8 @@ private: void install_ram() ATTR_COLD; required_device m_flash; - memory_view m_view_rom[2]; + memory_view m_view_rom_low; + memory_view m_view_rom_high; memory_view m_view_ram; memory_bank_array_creator<2> m_bank_rom; memory_bank_array_creator<2> m_bank_ram; @@ -126,7 +127,8 @@ mbc6_device::mbc6_device( device_t(mconfig, GB_ROM_MBC6, tag, owner, clock), device_gb_cart_interface(mconfig, *this), m_flash(*this, "flash"), - m_view_rom{ { *this, "romlow" }, { *this, "romhigh" } }, + m_view_rom_low(*this, "romlow"), + m_view_rom_high(*this, "romhigh" ), m_view_ram(*this, "ram"), m_bank_rom(*this, { "romlow", "romhigh" }), m_bank_ram(*this, { "ramlow", "ramhigh" }), @@ -148,8 +150,8 @@ image_init_result mbc6_device::load(std::string &message) return image_init_result::FAIL; // install views for ROM/flash and RAM - cart_space()->install_view(0x4000, 0x5fff, m_view_rom[0]); - cart_space()->install_view(0x6000, 0x7fff, m_view_rom[1]); + cart_space()->install_view(0x4000, 0x5fff, m_view_rom_low); + cart_space()->install_view(0x6000, 0x7fff, m_view_rom_high); cart_space()->install_view(0xa000, 0xbfff, m_view_ram); // set up ROM and RAM as appropriate @@ -180,11 +182,11 @@ image_init_result mbc6_device::load(std::string &message) write8sm_delegate(*this, FUNC(mbc6_device::select_flash))); // install Flash handlers - m_view_rom[0][1].install_readwrite_handler( + m_view_rom_low[1].install_readwrite_handler( 0x4000, 0x5fff, read8sm_delegate(*this, FUNC(mbc6_device::read_flash<0>)), write8sm_delegate(*this, FUNC(mbc6_device::write_flash<0>))); - m_view_rom[1][1].install_readwrite_handler( + m_view_rom_high[1].install_readwrite_handler( 0x6000, 0x7fff, read8sm_delegate(*this, FUNC(mbc6_device::read_flash<1>)), write8sm_delegate(*this, FUNC(mbc6_device::write_flash<1>))); @@ -227,8 +229,8 @@ void mbc6_device::device_reset() m_flash_enable = 0U; m_flash_writable = 0U; - m_view_rom[0].select(0); - m_view_rom[1].select(0); + m_view_rom_low.select(0); + m_view_rom_high.select(0); m_view_ram.disable(); if (m_bank_mask_rom) @@ -270,6 +272,7 @@ void mbc6_device::write_flash(offs_t offset, u8 data) void mbc6_device::bank_switch_rom(offs_t offset, u8 data) { auto const bank(BIT(offset, 12)); + memory_view &view(bank ? m_view_rom_high : m_view_rom_low); m_bank_sel_rom[bank] = data; if (!m_flash_select[bank]) @@ -280,11 +283,11 @@ void mbc6_device::bank_switch_rom(offs_t offset, u8 data) "%s: ROM bank %s unmapped\n", machine().describe_context(), bank ? "high" : "low"); - m_view_rom[bank].disable(); // is there a chip select for a second program ROM? + view.disable(); // is there a chip select for a second program ROM? } else { - m_view_rom[bank].select(0); + view.select(0); } } @@ -304,6 +307,7 @@ void mbc6_device::bank_switch_rom(offs_t offset, u8 data) void mbc6_device::select_flash(offs_t offset, u8 data) { auto const bank(BIT(offset, 12)); + memory_view &view(bank ? m_view_rom_high : m_view_rom_low); m_flash_select[bank] = BIT(data, 3); if (m_flash_select[bank]) { @@ -313,9 +317,9 @@ void mbc6_device::select_flash(offs_t offset, u8 data) bank ? "high" : "low", m_flash_enable ? "enabled" : "disabled"); if (m_flash_enable) - m_view_rom[bank].select(1); + view.select(1); else - m_view_rom[bank].disable(); + view.disable(); } else if (BIT(m_bank_sel_rom[bank], 7)) { @@ -323,7 +327,7 @@ void mbc6_device::select_flash(offs_t offset, u8 data) "%s: ROM bank %s unmapped\n", machine().describe_context(), bank ? "high" : "low"); - m_view_rom[bank].disable(); // is there a chip select for a second program ROM? + view.disable(); // is there a chip select for a second program ROM? } else { @@ -331,7 +335,7 @@ void mbc6_device::select_flash(offs_t offset, u8 data) "%s: ROM bank %s selected\n", machine().describe_context(), bank ? "high" : "low"); - m_view_rom[bank].select(0); + view.select(0); } // game writes 0xc6 when selecting ROM during boot - what do the other bits do? @@ -372,17 +376,17 @@ void mbc6_device::enable_flash(u8 data) { LOG("%s: Flash enabled\n", machine().describe_context()); if (m_flash_select[0]) - m_view_rom[0].select(1); + m_view_rom_low.select(1); if (m_flash_select[1]) - m_view_rom[1].select(1); + m_view_rom_high.select(1); } else { LOG("%s: Flash disabled\n", machine().describe_context()); if (m_flash_select[0]) - m_view_rom[0].disable(); + m_view_rom_low.disable(); if (m_flash_select[1]) - m_view_rom[1].disable(); + m_view_rom_high.disable(); } if (data & ~0x01) @@ -484,8 +488,8 @@ void mbc6_device::install_rom() if (!rombytes) { // just avoid fatal errors - m_view_rom[0][0]; - m_view_rom[1][0]; + m_view_rom_low[0]; + m_view_rom_high[0]; m_bank_mask_rom = 0U; } else if (PAGE_ROM_SIZE >= rombytes) @@ -507,8 +511,8 @@ void mbc6_device::install_rom() end, mirror); cart_space()->install_rom(begin, end, 0x2000 | mirror, &base[src]); - m_view_rom[0][0].install_rom(0x4000 | begin, 0x4000 | end, mirror, &base[src]); - m_view_rom[1][0].install_rom(0x6000 | begin, 0x6000 | end, mirror, &base[src]); + m_view_rom_low[0].install_rom(0x4000 | begin, 0x4000 | end, mirror, &base[src]); + m_view_rom_high[0].install_rom(0x6000 | begin, 0x6000 | end, mirror, &base[src]); }); m_bank_mask_rom = 0U; } @@ -533,8 +537,8 @@ void mbc6_device::install_rom() m_bank_rom[0]->configure_entry(entry, &base[page * PAGE_ROM_SIZE]); m_bank_rom[1]->configure_entry(entry, &base[page * PAGE_ROM_SIZE]); }); - m_view_rom[0][0].install_read_bank(0x4000, 0x5fff, m_bank_rom[0]); - m_view_rom[1][0].install_read_bank(0x6000, 0x7fff, m_bank_rom[1]); + m_view_rom_low[0].install_read_bank(0x4000, 0x5fff, m_bank_rom[0]); + m_view_rom_high[0].install_read_bank(0x6000, 0x7fff, m_bank_rom[1]); } } diff --git a/src/devices/bus/gameboy/slmulti.cpp b/src/devices/bus/gameboy/slmulti.cpp new file mode 100644 index 00000000000..0e3d1e6f05d --- /dev/null +++ b/src/devices/bus/gameboy/slmulti.cpp @@ -0,0 +1,235 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + Chinese multi-game cartridges by SL and possibly others + + Supports collections containing very large numbers of games designed for + MBC1 and MBC5 cartridges. + + ***************************************************************************/ + +#include "emu.h" +#include "slmulti.h" + +#include "cartbase.h" + +#include + +//#define VERBOSE 1 +//#define LOG_OUTPUT_FUNC osd_printf_info +#include "logmacro.h" + + +namespace bus::gameboy { + +namespace { + +class slmulti_device : public mbc_dual_uniform_device_base +{ +public: + slmulti_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); + + virtual image_init_result load(std::string &message) override ATTR_COLD; + +protected: + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + +private: + void bank_switch_rom_fine(u8 data); + void bank_switch_rom_coarse(u8 data); + void set_config_cmd(u8 data); + void do_config_cmd(u8 data); + + void update_bank_rom_high() + { + u16 const page(bank_rom_entry_high()); + LOG( + "%s: Set high ROM page 0x%03X (0x%06X)\n", + machine().describe_context(), + page, + u32(page) << 14); + set_bank_rom_high(page); + } + + u16 bank_rom_entry_high() const noexcept + { + u16 const hi(m_high_page & m_page_mask); + return (m_base_page & ~m_page_mask) | (hi ? hi : (m_zero_remap & m_page_mask)); + } + + memory_view m_view_ctrl; + + u16 m_base_page; + u16 m_high_page; + u16 m_page_mask; + u16 m_zero_remap; + u8 m_config_cmd; +}; + + +slmulti_device::slmulti_device( + machine_config const &mconfig, + char const *tag, + device_t *owner, + u32 clock) : + mbc_dual_uniform_device_base(mconfig, GB_ROM_SLMULTI, tag, owner, clock), + m_view_ctrl(*this, "ctrl"), + m_base_page(0U), + m_high_page(0U), + m_page_mask(0U), + m_zero_remap(0U), + m_config_cmd(0U) +{ +} + + +image_init_result slmulti_device::load(std::string &message) +{ + // set up ROM + set_bank_bits_rom(10); + if (!check_rom(message)) + return image_init_result::FAIL; + install_rom(); + + // install memory mapping control handlers + cart_space()->install_view( + 0x2000, 0x7fff, + m_view_ctrl); + cart_space()->install_write_handler( + 0x2000, 0x3fff, + write8smo_delegate(*this, FUNC(slmulti_device::bank_switch_rom_fine))); + + // this is for MBC5 games + m_view_ctrl[0].install_write_handler( + 0x3000, 0x3fff, + write8smo_delegate(*this, FUNC(slmulti_device::bank_switch_rom_coarse))); + + // install configuration handlers over the top + m_view_ctrl[1].install_write_handler( + 0x5000, 0x5fff, + write8smo_delegate(*this, FUNC(slmulti_device::set_config_cmd))); + m_view_ctrl[1].install_write_handler( + 0x7000, 0x7fff, + write8smo_delegate(*this, FUNC(slmulti_device::do_config_cmd))); + + // do this here - the menu program apparently does a system reset to get into DMG mode + m_view_ctrl.select(1); + + // all good + return image_init_result::PASS; +} + + +void slmulti_device::device_start() +{ + mbc_dual_uniform_device_base::device_start(); + + m_base_page = 0U; + m_high_page = 0U; + m_page_mask = 1U; + m_zero_remap = 0U; + m_config_cmd = 0U; + + save_item(NAME(m_base_page)); + save_item(NAME(m_high_page)); + save_item(NAME(m_page_mask)); + save_item(NAME(m_zero_remap)); + save_item(NAME(m_config_cmd)); +} + + +void slmulti_device::device_reset() +{ + mbc_dual_uniform_device_base::device_reset(); + + set_bank_rom_low(m_base_page & ~m_page_mask); + update_bank_rom_high(); +} + + +void slmulti_device::bank_switch_rom_fine(u8 data) +{ + m_high_page = data; + update_bank_rom_high(); +} + + +void slmulti_device::bank_switch_rom_coarse(u8 data) +{ + // there's no way to specify a 9-bit page mask, so MBC5 games larger than 4 MiB can't be supported + LOG("%s Set coarse ROM bank 0x%02X\n", machine().describe_context(), data); +} + + +void slmulti_device::set_config_cmd(u8 data) +{ + LOG("%s: Set configuration command 0x%02X\n", machine().describe_context(), data); + m_config_cmd = data; +} + + +void slmulti_device::do_config_cmd(u8 data) +{ + switch (m_config_cmd) + { + case 0x55: + // bit 4 unknown + m_base_page = (m_base_page & ~0x0200) | (u16(BIT(data, 3)) << 9); + m_page_mask = (2U << BIT(~data, 0, 3)) - 1; + switch (BIT(data, 5, 2)) + { + case 0x0: // used for MBC5 games + m_zero_remap = 0U; + m_view_ctrl.select(0); + break; + case 0x3: // used for MBC1 games + m_zero_remap = 1U; + m_view_ctrl.disable(); + break; + default: + logerror( + "%s: Unknown memory mapping mode 0x%X\n", + machine().describe_context(), + BIT(data, 5, 2)); + m_view_ctrl.disable(); + } + LOG( + "%s: Set base page = 0x%03X (0x%06X), page mask = 0x%03X, zero remap = 0x%03X%s\n", + machine().describe_context(), + m_base_page, + u32(m_base_page) << 14, + m_page_mask, + m_zero_remap, + BIT(data, 7) ? ", reset" : ""); + set_bank_rom_low(m_base_page & ~m_page_mask); + update_bank_rom_high(); + if (BIT(data, 7)) + machine().root_device().reset(); // TODO: expose reset line on cartridge interface + break; + + case 0xaa: + m_base_page = (m_base_page & ~0x01fe) | (u16(data) << 1); + LOG( + "%s: Set base page = 0x%03X (0x%06X)\n", + machine().describe_context(), + m_base_page, + u32(m_base_page) << 14); + break; + + default: + LOG( + "%s: Unknown configuration command 0x%02X with argument 0x%02X\n", + machine().describe_context(), + m_config_cmd, + data); + } +} + +} // anonymous namespace + +} // namespace bus::gameboy + + +DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_SLMULTI, device_gb_cart_interface, bus::gameboy::slmulti_device, "gb_rom_slmulti", "Game Boy SL Multi-Game Cartridge") diff --git a/src/devices/bus/gameboy/slmulti.h b/src/devices/bus/gameboy/slmulti.h new file mode 100644 index 00000000000..9ae716de72c --- /dev/null +++ b/src/devices/bus/gameboy/slmulti.h @@ -0,0 +1,18 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + Chinese multi-game cartridges by SL and possibly others + + ***************************************************************************/ +#ifndef MAME_BUS_GAMEBOY_SLMULTI_H +#define MAME_BUS_GAMEBOY_SLMULTI_H + +#pragma once + +#include "slot.h" + + +DECLARE_DEVICE_TYPE(GB_ROM_SLMULTI, device_gb_cart_interface) + +#endif // MAME_BUS_GAMEBOY_SLMULTI_H