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