summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/bus/gameboy/mbc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/bus/gameboy/mbc.cpp')
-rw-r--r--src/devices/bus/gameboy/mbc.cpp2488
1 files changed, 1307 insertions, 1181 deletions
diff --git a/src/devices/bus/gameboy/mbc.cpp b/src/devices/bus/gameboy/mbc.cpp
index bd57a4da23b..2db965839ad 100644
--- a/src/devices/bus/gameboy/mbc.cpp
+++ b/src/devices/bus/gameboy/mbc.cpp
@@ -1,11 +1,55 @@
// license:BSD-3-Clause
-// copyright-holders:Fabio Priuli, Wilbert Pol
+// copyright-holders:Vas Crabb, Wilbert Pol
/***********************************************************************************************************
Game Boy carts with MBC (Memory Bank Controller)
- TODO: RTC runs too fast while in-game, in MBC-3 games... find the problem!
+ MBC1 Mapper
+ ===========
+
+ The MBC1 mapper has two modes: 2MB ROM/8KB RAM or 512KB ROM/32KB RAM.
+ Initially, the mapper operates in 2MB ROM/8KB RAM mode.
+
+ 0000-1FFF - Writing to this area enables (value 0x0A) or disables (not 0x0A) the
+ SRAM.
+ 2000-3FFF - Writing a value 0bXXXBBBBB into the 2000-3FFF memory area selects the
+ lower 5 bits of the ROM bank to select for the 4000-7FFF memory area.
+ If a value of 0bXXX00000 is written then this will automatically be
+ changed to 0bXXX00001 by the mbc chip. Initial value 00.
+ 4000-5FFF - Writing a value 0bXXXXXXBB into the 4000-5FFF memory area either selects
+ the RAM bank to use or bits 6 and 7 for the ROM bank to use for the 4000-7FFF
+ memory area. This behaviour depends on the memory mode chosen.
+ These address lines are fixed in mode 1 and switch depending on A14 in mode 0.
+ In mode 0 these will drive 0 when RB 00 is accessed (A14 low) or the value set
+ in 4000-5FFF when RB <> 00 is accessed (A14 high).
+ Switching between modes does not clear this register. Initial value 00.
+ 6000-7FFF - Writing a value 0bXXXXXXXB into the 6000-7FFF memory area switches the mode.
+ B=0 - 2MB ROM/8KB RAM mode
+ B=1 - 512KB ROM/32KB RAM mode
+
+ Regular ROM aliasing rules apply.
+
+
+ MBC5 Mapper
+ ===========
+
+ 0000-1FFF - Writing to this area enables (0x0A) or disables (not 0x0A) the SRAM area.
+ 2000-2FFF - Writing to this area updates bits 7-0 of the ROM bank number to
+ appear at 4000-7FFF.
+ 3000-3FFF - Writing to this area updates bit 8 of the ROM bank number to appear
+ at 4000-7FFF.
+ 4000-5FFF - Writing to this area select the RAM bank number to use. If the
+ cartridge includes a Rumble Pack then bit 3 is used to control
+ rumble motor (0 - disable motor, 1 - enable motor).
+
+
+ TODO:
+ * MBC5 logo spoofing class implements several strategies. It's likely not all carts using it use all
+ the strategies. Strategies implemented by each cartridge should be identified.
+ * HK0701 and HK0819 seem to differ in that HK0819 fully decodes ROM addresses while HK0701 mirrors - we
+ should probably emulated the difference at some point.
+ * BC-R1616T3P is a variant of DSHGGB-81 with the same ROM scrambling but without logo spoofing.
***********************************************************************************************************/
@@ -13,1428 +57,1510 @@
#include "emu.h"
#include "mbc.h"
+#include "cartbase.ipp"
+#include "cartheader.h"
+#include "gbxfile.h"
-//-------------------------------------------------
-// gb_rom_mbc*_device - constructor
-//-------------------------------------------------
-
-DEFINE_DEVICE_TYPE(GB_ROM_MBC1, gb_rom_mbc1_device, "gb_rom_mbc1", "GB MBC1 Carts")
-DEFINE_DEVICE_TYPE(GB_ROM_MBC2, gb_rom_mbc2_device, "gb_rom_mbc2", "GB MBC2 Carts")
-DEFINE_DEVICE_TYPE(GB_ROM_MBC3, gb_rom_mbc3_device, "gb_rom_mbc3", "GB MBC3 Carts")
-DEFINE_DEVICE_TYPE(GB_ROM_MBC5, gb_rom_mbc5_device, "gb_rom_mbc5", "GB MBC5 Carts")
-DEFINE_DEVICE_TYPE(GB_ROM_MBC6, gb_rom_mbc6_device, "gb_rom_mbc6", "GB MBC6 Carts")
-DEFINE_DEVICE_TYPE(GB_ROM_MBC7, gb_rom_mbc7_device, "gb_rom_mbc7", "GB MBC7 Carts")
-DEFINE_DEVICE_TYPE(GB_ROM_M161, gb_rom_m161_device, "gb_rom_m161", "GB M161 Carts")
-DEFINE_DEVICE_TYPE(GB_ROM_MMM01, gb_rom_mmm01_device, "gb_rom_mmm01", "GB MMM01 Carts")
-DEFINE_DEVICE_TYPE(GB_ROM_SACHEN1, gb_rom_sachen_mmc1_device, "gb_rom_sachen1", "GB Sachen MMC1 Carts")
-DEFINE_DEVICE_TYPE(GB_ROM_SACHEN2, gb_rom_sachen_mmc2_device, "gb_rom_sachen2", "GB Sachen MMC2 Carts")
-DEFINE_DEVICE_TYPE(GB_ROM_188IN1, gb_rom_188in1_device, "gb_rom_188in1", "GB 188in1")
-DEFINE_DEVICE_TYPE(GB_ROM_SINTAX, gb_rom_sintax_device, "gb_rom_sintax", "GB MBC5 Sintax Carts")
-DEFINE_DEVICE_TYPE(GB_ROM_CHONGWU, gb_rom_chongwu_device, "gb_rom_chongwu", "GB Chong Wu Xiao Jing Ling")
-DEFINE_DEVICE_TYPE(GB_ROM_LICHENG, gb_rom_licheng_device, "gb_rom_licheng", "GB MBC5 Li Cheng Carts")
-DEFINE_DEVICE_TYPE(GB_ROM_DIGIMON, gb_rom_digimon_device, "gb_rom_digimon", "GB Digimon")
-DEFINE_DEVICE_TYPE(GB_ROM_ROCKMAN8, gb_rom_rockman8_device, "gb_rom_rockman8", "GB MBC1 Rockman 8")
-DEFINE_DEVICE_TYPE(GB_ROM_SM3SP, gb_rom_sm3sp_device, "gb_sm3sp", "GB MBC1 Super Mario 3 Special")
-DEFINE_DEVICE_TYPE(GB_ROM_CAMERA, gb_rom_camera_device, "gb_rom_camera", "GB Camera")
-
-
-gb_rom_mbc_device::gb_rom_mbc_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, type, tag, owner, clock)
- , device_gb_cart_interface(mconfig, *this)
- , m_ram_enable(0)
-{
-}
-
-gb_rom_mbc1_device::gb_rom_mbc1_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, type, tag, owner, clock)
- , m_mode(MODE_16M_64k)
- , m_mask(0x1f)
- , m_shift(0)
-{
-}
-
-gb_rom_mbc1_device::gb_rom_mbc1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc1_device(mconfig, GB_ROM_MBC1, tag, owner, clock)
-{
-}
-
-gb_rom_mbc2_device::gb_rom_mbc2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, GB_ROM_MBC2, tag, owner, clock)
-{
-}
+#include "bus/generic/slot.h"
-gb_rom_mbc3_device::gb_rom_mbc3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, GB_ROM_MBC3, tag, owner, clock)
- , m_rtc_ready(0)
-{
-}
+#include "corestr.h"
-gb_rom_mbc5_device::gb_rom_mbc5_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, type, tag, owner, clock)
-{
-}
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <locale>
+#include <sstream>
+#include <type_traits>
-gb_rom_mbc5_device::gb_rom_mbc5_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc5_device(mconfig, GB_ROM_MBC5, tag, owner, clock)
-{
-}
+//#define VERBOSE 1
+//#define LOG_OUTPUT_FUNC osd_printf_info
+#include "logmacro.h"
-gb_rom_mbc6_device::gb_rom_mbc6_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, GB_ROM_MBC6, tag, owner, clock), m_latch1(0), m_latch2(0), m_bank_4000(0), m_bank_6000(0)
-{
-}
-gb_rom_mbc7_device::gb_rom_mbc7_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, GB_ROM_MBC7, tag, owner, clock)
-{
-}
+namespace bus::gameboy {
-gb_rom_m161_device::gb_rom_m161_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, GB_ROM_M161, tag, owner, clock), m_base_bank(0), m_load_disable(0)
-{
-}
+namespace {
-gb_rom_mmm01_device::gb_rom_mmm01_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, GB_ROM_MMM01, tag, owner, clock), m_romb(0), m_romb_nwe(0), m_ramb(0), m_ramb_nwe(0), m_mode(0), m_mode_nwe(0), m_map(0), m_mux(0)
-{
-}
+//**************************************************************************
+// MBC1/MBC3/MBC5 banked RAM support helper class
+//**************************************************************************
-gb_rom_sachen_mmc1_device::gb_rom_sachen_mmc1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_sachen_mmc1_device(mconfig, GB_ROM_SACHEN1, tag, owner, clock)
+class rom_mbc_device_base : public mbc_ram_device_base<mbc_dual_device_base>
{
-}
-
-gb_rom_sachen_mmc1_device::gb_rom_sachen_mmc1_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, type, tag, owner, clock), m_base_bank(0), m_mask(0), m_mode(0), m_unlock_cnt(0)
-{
-}
-
-gb_rom_sachen_mmc2_device::gb_rom_sachen_mmc2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_sachen_mmc1_device(mconfig, GB_ROM_SACHEN2, tag, owner, clock)
-{
-}
+protected:
+ rom_mbc_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock) :
+ mbc_ram_device_base<mbc_dual_device_base>(mconfig, type, tag, owner, clock),
+ m_view_ram(*this, "ram"),
+ m_bank_lines{ 0U, 0U }
+ {
+ }
-gb_rom_188in1_device::gb_rom_188in1_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc1_device(mconfig, GB_ROM_188IN1, tag, owner, clock), m_game_base(0)
-{
-}
+ std::error_condition install_memory(std::string &message, unsigned highbits, unsigned lowbits) ATTR_COLD
+ {
+ // see if the cartridge shouldn't use all the low bank bits
+ char const *const lowbitsfeature(get_feature("banklowbits"));
+ if (lowbitsfeature)
+ {
+ std::istringstream s;
+ s.imbue(std::locale::classic());
+ s.str(lowbitsfeature);
+ int b(-1);
+ if (!(s >> b) || (0 > b) || (lowbits < b))
+ {
+ message = util::string_format(
+ "Invalid 'banklowbits' value (must be a number from 0 to %u)\n",
+ lowbits);
+ return image_error::BADSOFTWARE;
+ }
+ lowbits = b;
+ }
-gb_rom_sintax_device::gb_rom_sintax_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, GB_ROM_SINTAX, tag, owner, clock), m_bank_mask(0), m_bank(0), m_reg(0), m_currentxor(0), m_xor2(0), m_xor3(0), m_xor4(0), m_xor5(0), m_sintax_mode(0)
-{
-}
+ // check for valid ROM/RAM regions
+ set_bank_bits_rom(highbits, lowbits);
+ set_bank_bits_ram(highbits);
+ if (!check_rom(message) || !check_ram(message))
+ return image_error::BADSOFTWARE;
+
+ // everything checked out - install memory and return success
+ m_bank_lines[0] = util::make_bitmask<u16>(lowbits);
+ m_bank_lines[1] = util::make_bitmask<u16>(highbits);
+ cart_space()->install_view(0xa000, 0xbfff, m_view_ram);
+ install_rom();
+ install_ram(m_view_ram[0]);
+ return std::error_condition();
+ }
-gb_rom_chongwu_device::gb_rom_chongwu_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc5_device(mconfig, GB_ROM_CHONGWU, tag, owner, clock), m_protection_checked(0)
-{
-}
+ void set_bank_rom_fine(u16 entry)
+ {
+ mbc_ram_device_base<mbc_dual_device_base>::set_bank_rom_fine(entry & m_bank_lines[0]);
+ }
-gb_rom_licheng_device::gb_rom_licheng_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc5_device(mconfig, GB_ROM_LICHENG, tag, owner, clock)
-{
-}
+ void set_ram_enable(bool enable)
+ {
+ LOG(
+ "%s: Cartridge RAM %s\n",
+ machine().describe_context(),
+ enable ? "enabled" : "disabled");
+ if (enable)
+ m_view_ram.select(0);
+ else
+ m_view_ram.disable();
+ }
-gb_rom_digimon_device::gb_rom_digimon_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc5_device(mconfig, GB_ROM_DIGIMON, tag, owner, clock)
-{
-}
+ void bank_switch_coarse(u8 data)
+ {
+ set_bank_rom_coarse(data & m_bank_lines[1]);
+ set_bank_ram(data & m_bank_lines[1]);
+ }
-gb_rom_rockman8_device::gb_rom_rockman8_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, GB_ROM_ROCKMAN8, tag, owner, clock), m_bank_mask(0), m_bank(0), m_reg(0)
-{
-}
+private:
+ memory_view m_view_ram;
-gb_rom_sm3sp_device::gb_rom_sm3sp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, GB_ROM_SM3SP, tag, owner, clock), m_bank_mask(0), m_bank(0), m_reg(0), m_mode(0)
-{
-}
+ u16 m_bank_lines[2];
+};
-gb_rom_camera_device::gb_rom_camera_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : gb_rom_mbc_device(mconfig, GB_ROM_CAMERA, tag, owner, clock)
-{
-}
-//-------------------------------------------------
-// shared_start
-//-------------------------------------------------
+//**************************************************************************
+// MBC5 and clones (max 128 MiB ROM, max 128 KiB SRAM)
+//**************************************************************************
-void gb_rom_mbc_device::shared_start()
+class mbc5_device_base : public rom_mbc_device_base
{
- save_item(NAME(m_latch_bank));
- save_item(NAME(m_latch_bank2));
- save_item(NAME(m_ram_bank));
- save_item(NAME(m_ram_enable));
-}
+public:
+ virtual std::error_condition load(std::string &message) override ATTR_COLD
+ {
+ // set up ROM and RAM
+ std::error_condition err = install_memory(message);
+ if (err)
+ return err;
+
+ // install handlers
+ cart_space()->install_write_handler(
+ 0x0000, 0x1fff,
+ emu::rw_delegate(*this, FUNC(mbc5_device_base::enable_ram)));
+ cart_space()->install_write_handler(
+ 0x2000, 0x2fff,
+ emu::rw_delegate(*this, FUNC(mbc5_device_base::bank_switch_fine_low)));
+ cart_space()->install_write_handler(
+ 0x3000, 0x3fff,
+ emu::rw_delegate(*this, FUNC(mbc5_device_base::bank_switch_fine_high)));
+ cart_space()->install_write_handler(
+ 0x4000, 0x5fff,
+ emu::rw_delegate(*this, FUNC(mbc5_device_base::bank_switch_coarse)));
+
+ // all good
+ return std::error_condition();
+ }
-//-------------------------------------------------
-// shared_reset
-//-------------------------------------------------
+protected:
+ mbc5_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock) :
+ rom_mbc_device_base(mconfig, type, tag, owner, clock)
+ {
+ }
-void gb_rom_mbc_device::shared_reset()
-{
- m_latch_bank = 0;
- m_latch_bank2 = 1;
- m_ram_bank = 0;
- m_ram_enable = 0;
-}
+ virtual void device_reset() override ATTR_COLD
+ {
+ rom_mbc_device_base::device_reset();
-//-------------------------------------------------
-// mapper specific start/reset
-//-------------------------------------------------
+ set_bank_rom_coarse(0);
+ set_bank_rom_fine(0);
+ set_ram_enable(false);
+ set_bank_ram(0);
+ }
-void gb_rom_mbc3_device::device_start()
-{
- shared_start();
- save_item(NAME(m_rtc_regs));
- save_item(NAME(m_rtc_ready));
-}
+ std::error_condition install_memory(std::string &message) ATTR_COLD
+ {
+ return rom_mbc_device_base::install_memory(message, 4, 9);
+ }
-void gb_rom_mbc3_device::device_reset()
-{
- shared_reset();
- memset(m_rtc_regs, 0, sizeof(m_rtc_regs));
- m_rtc_ready = 0;
-}
+ void enable_ram(u8 data)
+ {
+ set_ram_enable(0x0a == data);
+ }
-void gb_rom_mbc6_device::device_start()
-{
- save_item(NAME(m_bank_4000));
- save_item(NAME(m_bank_6000));
- save_item(NAME(m_latch1));
- save_item(NAME(m_latch2));
- save_item(NAME(m_latch_bank));
- save_item(NAME(m_latch_bank2));
- save_item(NAME(m_ram_bank));
- save_item(NAME(m_ram_enable));
-}
-
-void gb_rom_mbc6_device::device_reset()
-{
- m_bank_4000 = 2; // correct default?
- m_bank_6000 = 3; // correct default?
- m_latch1 = 0; // correct default?
- m_latch2 = 0; // correct default?
-
- m_latch_bank = 2; // correct default?
- m_latch_bank2 = 3; // correct default?
- m_ram_bank = 0;
- m_ram_enable = 0;
-}
-
-void gb_rom_m161_device::device_start()
-{
- shared_start();
- save_item(NAME(m_base_bank));
- save_item(NAME(m_load_disable));
-}
+ void bank_switch_fine_low(u8 data)
+ {
+ set_bank_rom_fine((bank_rom_fine() & 0x0100) | u16(data));
+ }
-void gb_rom_m161_device::device_reset()
-{
- shared_reset();
- m_base_bank = 0;
- m_load_disable = 0;
-}
+ void bank_switch_fine_high(u8 data)
+ {
+ set_bank_rom_fine((bank_rom_fine() & 0x00ff) | (u16(data & 0x01) << 8));
+ }
+};
-void gb_rom_mmm01_device::device_start()
-{
- shared_start();
- save_item(NAME(m_romb));
- save_item(NAME(m_romb_nwe));
- save_item(NAME(m_ramb));
- save_item(NAME(m_ramb_nwe));
- save_item(NAME(m_mode));
- save_item(NAME(m_mode_nwe));
- save_item(NAME(m_map));
- save_item(NAME(m_mux));
-}
-
-void gb_rom_mmm01_device::device_reset()
-{
- m_romb = 0x000;
- m_romb_nwe = 0x00;
- m_ramb = 0x00;
- m_ramb_nwe = 0x00;
- m_mode = 0x00;
- m_map = 0x00;
- m_mux = 0x00;
-}
-
-void gb_rom_sachen_mmc1_device::device_start()
-{
- shared_start();
- save_item(NAME(m_base_bank));
- save_item(NAME(m_mask));
- save_item(NAME(m_mode));
- save_item(NAME(m_unlock_cnt));
-}
-
-void gb_rom_sachen_mmc1_device::device_reset()
-{
- shared_reset();
- m_base_bank = 0x00;
- m_mask = 0x00;
- m_mode = MODE_LOCKED;
- m_unlock_cnt = 0x00;
-}
-
-void gb_rom_sachen_mmc2_device::device_start()
-{
- shared_start();
- save_item(NAME(m_base_bank));
- save_item(NAME(m_mask));
- save_item(NAME(m_mode));
- save_item(NAME(m_unlock_cnt));
-}
-
-void gb_rom_sachen_mmc2_device::device_reset()
-{
- shared_reset();
- m_base_bank = 0x00;
- m_mask = 0x00;
- m_mode = MODE_LOCKED_DMG;
- m_unlock_cnt = 0x00;
-}
-
-void gb_rom_sintax_device::device_start()
-{
- shared_start();
- save_item(NAME(m_sintax_mode));
- save_item(NAME(m_currentxor));
- save_item(NAME(m_xor2));
- save_item(NAME(m_xor3));
- save_item(NAME(m_xor4));
- save_item(NAME(m_xor5));
-}
-
-void gb_rom_sintax_device::device_reset()
-{
- shared_reset();
- m_sintax_mode = 0;
- m_currentxor = 0;
- m_xor2 = 0;
- m_xor3 = 0;
- m_xor4 = 0;
- m_xor5 = 0;
-}
-
-void gb_rom_chongwu_device::device_start()
-{
- shared_start();
- save_item(NAME(m_protection_checked));
-}
-void gb_rom_chongwu_device::device_reset()
-{
- shared_reset();
- m_protection_checked = 0;
-}
-void gb_rom_camera_device::device_start()
-{
- shared_start();
- save_item(NAME(m_camera_regs));
-}
+//**************************************************************************
+// MBC5-like pirate cartridges with logo spoofing
+//**************************************************************************
-void gb_rom_camera_device::device_reset()
+class mbc5_logo_spoof_device_base : public mbc5_device_base
{
- shared_reset();
- memset(m_camera_regs, 0, sizeof(m_camera_regs));
-}
-
-
-/*-------------------------------------------------
- mapper specific handlers
- -------------------------------------------------*/
+public:
+ virtual std::error_condition load(std::string &message) override ATTR_COLD
+ {
+ // install regular MBC5 handlers
+ std::error_condition const result(mbc5_device_base::load(message));
+ if (result)
+ return result;
+
+ // intercept ROM reads for logo spoofing
+ cart_space()->install_read_handler(
+ 0x0000, 0x7fff,
+ emu::rw_delegate(*this, FUNC(mbc5_logo_spoof_device_base::read_rom)));
+
+ // all good
+ return std::error_condition();
+ }
-uint8_t gb_rom_mbc_device::read_rom(offs_t offset)
-{
- return m_rom[rom_bank_map[m_latch_bank] + offset];
-}
+protected:
+ mbc5_logo_spoof_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock) :
+ mbc5_device_base(mconfig, type, tag, owner, clock),
+ m_ram_tap(),
+ m_notif_cart_space(),
+ m_counter(0U),
+ m_spoof_logo(0U),
+ m_installing_tap(false)
+ {
+ }
-uint8_t gb_rom_mbc_device::read_ram(offs_t offset)
-{
- if (!m_ram.empty())
- return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + offset];
- else
- return 0xff;
-}
+ virtual void device_start() override ATTR_COLD
+ {
+ mbc5_device_base::device_start();
-void gb_rom_mbc_device::write_ram(offs_t offset, uint8_t data)
-{
- if (!m_ram.empty())
- m_ram[ram_bank_map[m_ram_bank] * 0x2000 + offset] = data;
-}
+ save_item(NAME(m_counter));
+ save_item(NAME(m_spoof_logo));
+ }
+ virtual void device_reset() override ATTR_COLD
+ {
+ mbc5_device_base::device_reset();
-// MBC1
+ m_counter = 0U;
+ m_spoof_logo = 0U;
-uint8_t gb_rom_mbc1_device::read_rom(offs_t offset)
-{
- if (offset & 0x4000) /* RB1 */
- return m_rom[rom_bank_map[(m_ram_bank << (5 + m_shift)) | m_latch_bank2] * 0x4000 + (offset & 0x3fff)];
- else
- { /* RB0 */
- int bank = (m_mode == MODE_4M_256k) ? (m_ram_bank << (5 + m_shift)) : 0;
- return m_rom[rom_bank_map[bank] * 0x4000 + (offset & 0x3fff)];
+ install_ram_tap();
+ add_ram_notifier();
}
-}
-void gb_rom_mbc1_device::write_bank(offs_t offset, uint8_t data)
-{
- // the mapper only uses inputs A15..A13
- switch (offset & 0xe000)
+ virtual void device_post_load() override ATTR_COLD
{
- case 0x0000: // RAM Enable Register
- m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0;
- break;
- case 0x2000: // ROM Bank Register
- data &= 0x1f;
- m_latch_bank2 = data ? data : 1;
- m_latch_bank2 &= m_mask;
- break;
- case 0x4000: // RAM Bank Register
- m_ram_bank = data & 0x3;
- break;
- case 0x6000: // MBC1 Mode Register
- m_mode = (data & 0x1) ? MODE_4M_256k : MODE_16M_64k;
- break;
- }
-}
+ mbc5_device_base::device_post_load();
-uint8_t gb_rom_mbc1_device::read_ram(offs_t offset)
-{
- if (!m_ram.empty() && m_ram_enable)
- {
- int bank = (m_mode == MODE_4M_256k) ? m_ram_bank : 0;
- return m_ram[ram_bank_map[bank] * 0x2000 + offset];
+ install_ram_tap();
+ if (!m_spoof_logo)
+ add_ram_notifier();
}
- else
- return 0xff;
-}
-void gb_rom_mbc1_device::write_ram(offs_t offset, uint8_t data)
-{
- if (!m_ram.empty() && m_ram_enable)
+ u8 read_rom(offs_t offset)
{
- int bank = (m_mode == MODE_4M_256k) ? m_ram_bank : 0;
- m_ram[ram_bank_map[bank] * 0x2000 + offset] = data;
+ offset = rom_access(offset);
+ return (BIT(offset, 14) ? bank_rom_high_base() : bank_rom_low_base())[offset & 0x3fff];
}
-}
+ offs_t rom_access(offs_t offset)
+ {
+ if (!machine().side_effects_disabled() && (3U > m_spoof_logo))
+ {
+ // These cartridges work by counting low-to-high transitions on A15.
+ // CPU keeps A15 high while reading internal bootstrap ROM.
+ // Doing this on ROM reads is a good enough approximation for it to work.
+ if (0x30 != m_counter)
+ {
+ ++m_counter;
+ }
+ else
+ {
+ switch (m_spoof_logo)
+ {
+ case 0U:
+ m_counter = 1U;
+ [[fallthrough]];
+ case 1U:
+ ++m_spoof_logo;
+ m_notif_cart_space.reset();
+ m_ram_tap.remove();
+ LOG(
+ "%s: Spoof logo step %u\n",
+ machine().describe_context(),
+ m_spoof_logo);
+ }
+ }
-// MBC2
-
-uint8_t gb_rom_mbc2_device::read_rom(offs_t offset)
-{
- if (offset & 0x4000) /* RB1 */
- return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)];
- else /* RB0 */
- return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)];
-}
+ // accessing entry point disables logo spoofing logic
+ if (0x0100 == offset)
+ m_spoof_logo = 3U;
+ }
-void gb_rom_mbc2_device::write_bank(offs_t offset, uint8_t data)
-{
- // the mapper only has data lines D3..D0
- data &= 0x0f;
+ // A7 is held high to show pirate logo
+ if (m_spoof_logo && ((2U != m_spoof_logo) || (cartheader::OFFSET_LOGO > offset) || (cartheader::OFFSET_TITLE <= offset)))
+ return offset;
+ else
+ return offset | 0x0080;
+ }
- // the mapper only uses inputs A15..A14, A8 for register accesses
- switch (offset & 0xc100)
+private:
+ void install_ram_tap()
{
- case 0x0000: // RAM Enable Register
- m_ram_enable = (data == 0x0a) ? 1 : 0;
- break;
- case 0x0100: // ROM Bank Register
- m_latch_bank2 = (data == 0x00) ? 0x01 : data;
- break;
+ if (!m_installing_tap)
+ {
+ m_installing_tap = true;
+ m_ram_tap.remove();
+ if (!m_spoof_logo)
+ {
+ m_ram_tap = cart_space()->install_write_tap(
+ 0xc000, 0xdfff,
+ "ram_tap_w",
+ [this] (offs_t offset, u8 &data, u8 mem_mask)
+ {
+ assert(!m_spoof_logo);
+
+ if (!machine().side_effects_disabled())
+ {
+ m_counter = 0U;
+ ++m_spoof_logo;
+ LOG(
+ "%s: Spoof logo step %u\n",
+ machine().describe_context(),
+ m_spoof_logo);
+ m_notif_cart_space.reset();
+ m_ram_tap.remove();
+ }
+ },
+ &m_ram_tap);
+ }
+ else
+ {
+ m_notif_cart_space.reset();
+ }
+ m_installing_tap = false;
+ }
}
-}
-uint8_t gb_rom_mbc2_device::read_ram(offs_t offset)
-{
- if (!m_ram.empty() && m_ram_enable)
- return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x01ff)] | 0xf0;
- else
- return 0xff;
-}
-
-void gb_rom_mbc2_device::write_ram(offs_t offset, uint8_t data)
-{
- if (!m_ram.empty() && m_ram_enable)
- m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x01ff)] = data & 0x0f;
-}
+ void add_ram_notifier()
+ {
+ m_notif_cart_space = cart_space()->add_change_notifier(
+ [this] (read_or_write mode)
+ {
+ if (u32(mode) & u32(read_or_write::WRITE))
+ install_ram_tap();
+ });
+ }
+ memory_passthrough_handler m_ram_tap;
+ util::notifier_subscription m_notif_cart_space;
-// MBC3
+ u8 m_counter;
+ u8 m_spoof_logo;
+ bool m_installing_tap;
+};
-void gb_rom_mbc3_device::update_rtc()
-{
- system_time curtime;
- machine().current_datetime(curtime);
- m_rtc_regs[0] = curtime.local_time.second;
- m_rtc_regs[1] = curtime.local_time.minute;
- m_rtc_regs[2] = curtime.local_time.hour;
- m_rtc_regs[3] = curtime.local_time.day & 0xff;
- m_rtc_regs[4] = (m_rtc_regs[4] & 0xf0) | (curtime.local_time.day >> 8);
-}
-uint8_t gb_rom_mbc3_device::read_rom(offs_t offset)
-{
- if (offset < 0x4000)
- return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)];
- else
- return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)];
-}
+//**************************************************************************
+// MBC5-like pirate cartridges with scrambled bank numbers and data
+//**************************************************************************
-void gb_rom_mbc3_device::write_bank(offs_t offset, uint8_t data)
+class mbc5_scrambled_device_base : public mbc5_logo_spoof_device_base
{
- if (offset < 0x2000)
- m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0;
- else if (offset < 0x4000)
+ virtual std::error_condition load(std::string &message) override ATTR_COLD
{
- // 7bits
- data &= 0x7f;
- /* Selecting bank 0 == selecting bank 1 */
- if (data == 0)
- data = 1;
-
- m_latch_bank2 = data;
+ // install regular MBC5 handlers
+ std::error_condition const result(mbc5_logo_spoof_device_base::load(message));
+ if (result)
+ return result;
+
+ // bank numbers and ROM contents are scrambled for protection
+ cart_space()->install_read_handler(
+ 0x0000, 0x3fff,
+ emu::rw_delegate(*this, FUNC(mbc5_scrambled_device_base::read_rom_low)));
+ cart_space()->install_read_handler(
+ 0x4000, 0x7fff,
+ emu::rw_delegate(*this, FUNC(mbc5_scrambled_device_base::read_rom_high)));
+ cart_space()->install_write_handler(
+ 0x2000, 0x2000, 0x0000, 0x0f00, 0x0000,
+ emu::rw_delegate(*this, FUNC(mbc5_scrambled_device_base::bank_switch_fine_low_scrambled)));
+ cart_space()->install_write_handler(
+ 0x2001, 0x2001, 0x0000, 0x0f00, 0x0000,
+ emu::rw_delegate(*this, FUNC(mbc5_scrambled_device_base::set_data_scramble)));
+ cart_space()->install_write_handler(
+ 0x2080, 0x2080, 0x0000, 0x0f00, 0x0000,
+ emu::rw_delegate(*this, FUNC(mbc5_scrambled_device_base::set_bank_scramble)));
+
+ // all good
+ return std::error_condition();
}
- else if (offset < 0x6000)
+
+protected:
+ using unscramble_function = u8 (*)(u8);
+
+ mbc5_scrambled_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock) :
+ mbc5_logo_spoof_device_base(mconfig, type, tag, owner, clock),
+ m_unscramble_data(nullptr),
+ m_unscramble_bank(nullptr),
+ m_scramble_mode{ 0U, 0U }
{
- m_ram_bank = data;
}
- else if (has_timer)
+
+ virtual void device_start() override ATTR_COLD
{
- if (m_rtc_ready == 1 && data == 0)
- m_rtc_ready = 0;
- if (m_rtc_ready == 0 && data == 1)
- {
- m_rtc_ready = 1;
- update_rtc();
- }
+ mbc5_logo_spoof_device_base::device_start();
+
+ save_item(NAME(m_scramble_mode));
}
-}
-uint8_t gb_rom_mbc3_device::read_ram(offs_t offset)
-{
- if (m_ram_bank < 4 && m_ram_enable)
+ virtual void device_reset() override ATTR_COLD
{
- // RAM
- if (!m_ram.empty())
- return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)];
+ mbc5_logo_spoof_device_base::device_reset();
+
+ m_unscramble_data = unscramble_data(0U);
+ m_unscramble_bank = unscramble_bank(0U);
+ m_scramble_mode[0] = 0U;
+ m_scramble_mode[1] = 0U;
}
- if (m_ram_bank >= 0x8 && m_ram_bank <= 0xc)
+
+ virtual void device_post_load() override ATTR_COLD
{
- // RTC registers
- if (has_timer)
- return m_rtc_regs[m_ram_bank - 8];
+ mbc5_logo_spoof_device_base::device_post_load();
+
+ m_unscramble_data = unscramble_data(m_scramble_mode[0]);
+ m_unscramble_bank = unscramble_bank(m_scramble_mode[1]);
}
- return 0xff;
-}
-void gb_rom_mbc3_device::write_ram(offs_t offset, uint8_t data)
-{
- if (m_ram_bank < 4 && m_ram_enable)
+ virtual unscramble_function unscramble_data(u8 data) = 0;
+ virtual unscramble_function unscramble_bank(u8 data) = 0;
+
+ template <unsigned... B>
+ static u8 unscramble(u8 data)
{
- // RAM
- if (!m_ram.empty())
- m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data;
+ return bitswap<8>(data, B...);
}
- if (m_ram_bank >= 0x8 && m_ram_bank <= 0xc && m_ram_enable)
+
+private:
+ u8 read_rom_low(offs_t offset)
{
- // RTC registers are writeable too
- if (has_timer)
- m_rtc_regs[m_ram_bank - 8] = data;
+ offset = rom_access(offset);
+ return bank_rom_low_base()[offset];
}
-}
-
-// MBC5
-uint8_t gb_rom_mbc5_device::read_rom(offs_t offset)
-{
- if (offset < 0x4000)
- return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)];
- else
- return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)];
-}
+ u8 read_rom_high(offs_t offset)
+ {
+ offset = rom_access(0x4000 | offset) & 0x3fff;
+ return m_unscramble_data(bank_rom_high_base()[offset]);
+ }
-void gb_rom_mbc5_device::write_bank(offs_t offset, uint8_t data)
-{
- if (offset < 0x2000)
- m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0;
- else if (offset < 0x3000)
+ void bank_switch_fine_low_scrambled(u8 data)
{
- // MBC5 has a 9 bit bank select
- // Writing into 2000-2fff sets the lower 8 bits
- m_latch_bank2 = (m_latch_bank2 & 0x100) | data;
+ set_bank_rom_fine((bank_rom_fine() & 0x0100) | u16(m_unscramble_bank(data)));
}
- else if (offset < 0x4000)
+
+ void set_data_scramble(u8 data)
{
- // MBC5 has a 9 bit bank select
- // Writing into 3000-3fff sets the 9th bit
- m_latch_bank2 = (m_latch_bank2 & 0xff) | ((data & 0x01) << 8);
+ LOG(
+ "%s: Set ROM data scrambling mode 0x%02X\n",
+ machine().describe_context(),
+ data);
+ if (data != m_scramble_mode[0])
+ {
+ m_unscramble_data = unscramble_data(data);
+ m_scramble_mode[0] = data;
+ }
+
+ // TODO: does this actually trigger a bank switch?
+ set_bank_rom_fine((bank_rom_fine() & 0x0100) | u16(data));
}
- else if (offset < 0x6000)
+
+ void set_bank_scramble(u8 data)
{
- data &= 0x0f;
- if (has_rumble)
+ LOG(
+ "%s: Set ROM bank number scrambling mode 0x%02X\n",
+ machine().describe_context(),
+ data);
+ if (data != m_scramble_mode[1])
{
- machine().output().set_value("Rumble", BIT(data, 3));
- data &= 0x7;
+ m_unscramble_bank = unscramble_bank(data);
+ m_scramble_mode[1] = data;
}
- m_ram_bank = data;
+
+ // TODO: does this actually trigger a bank switch?
+ set_bank_rom_fine((bank_rom_fine() & 0x0100) | u16(data));
}
-}
-uint8_t gb_rom_mbc5_device::read_ram(offs_t offset)
-{
- if (!m_ram.empty() && m_ram_enable)
- return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)];
- else
- return 0xff;
-}
+ unscramble_function m_unscramble_data;
+ unscramble_function m_unscramble_bank;
-void gb_rom_mbc5_device::write_ram(offs_t offset, uint8_t data)
-{
- if (!m_ram.empty() && m_ram_enable)
- m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data;
-}
+ u8 m_scramble_mode[2];
+};
-// MBC6
-uint8_t gb_rom_mbc6_device::read_rom(offs_t offset)
-{
- if (offset < 0x4000)
- return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)];
- else if (offset < 0x6000)
- return m_rom[rom_bank_map[m_bank_4000 >> 1] * 0x4000 + (m_bank_4000 & 0x01) * 0x2000 + (offset & 0x1fff)];
- else
- return m_rom[rom_bank_map[m_bank_6000 >> 1] * 0x4000 + (m_bank_6000 & 0x01) * 0x2000 + (offset & 0x1fff)];
-}
-
-void gb_rom_mbc6_device::write_bank(offs_t offset, uint8_t data)
+
+//**************************************************************************
+// MBC1 (max 2 MiB ROM, max 32 KiB SRAM)
+//**************************************************************************
+
+class mbc1_device : public rom_mbc_device_base
{
- if (offset < 0x2000)
+public:
+ mbc1_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ rom_mbc_device_base(mconfig, GB_ROM_MBC1, tag, owner, clock)
{
- logerror( "%s write to mbc6 ram enable area: %04X <- 0x%02X\n", machine().describe_context(), offset, data );
}
- else if (offset < 0x3000)
+
+ virtual std::error_condition load(std::string &message) override ATTR_COLD
{
- if (!(offset & 0x0800))
- m_latch1 = data;
- else if (data == 0x00)
- m_bank_4000 = m_latch1;
+ // probe for "collection" cartridges wired for 4-bit fine bank number
+ unsigned banklowbits(5U);
+ if (is_collection())
+ banklowbits = 4U;
+
+ // set up ROM and RAM
+ std::error_condition err = install_memory(message, 2, banklowbits);
+ if (err)
+ return err;
+
+ // install handlers
+ cart_space()->install_write_handler(
+ 0x0000, 0x1fff,
+ emu::rw_delegate(*this, FUNC(mbc1_device::enable_ram)));
+ cart_space()->install_write_handler(
+ 0x2000, 0x3fff,
+ emu::rw_delegate(*this, FUNC(mbc1_device::bank_switch_fine)));
+ cart_space()->install_write_handler(
+ 0x4000, 0x5fff,
+ emu::rw_delegate(*this, FUNC(mbc1_device::bank_switch_coarse)));
+ cart_space()->install_write_handler(
+ 0x6000, 0x7fff,
+ emu::rw_delegate(*this, FUNC(mbc1_device::bank_low_mask)));
+
+ // all good
+ return std::error_condition();
}
- else if (offset < 0x4000)
+
+protected:
+ virtual void device_reset() override ATTR_COLD
{
- if (!(offset & 0x0800))
- m_latch2 = data;
- else if (data == 0x00)
- m_bank_6000 = m_latch2;
+ rom_mbc_device_base::device_reset();
+
+ set_bank_rom_coarse(0);
+ set_bank_rom_fine(1);
+ set_bank_rom_low_coarse_mask(0x00);
+ set_ram_enable(false);
+ set_bank_ram(0);
+ set_bank_ram_mask(0x00);
}
-}
-
-uint8_t gb_rom_mbc6_device::read_ram(offs_t offset)
-{
- if (!m_ram.empty())
- return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)];
- else
- return 0xff;
-}
-
-void gb_rom_mbc6_device::write_ram(offs_t offset, uint8_t data)
-{
- if (!m_ram.empty())
- m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data;
-}
-
-// MBC7
-uint8_t gb_rom_mbc7_device::read_rom(offs_t offset)
-{
- if (offset < 0x4000)
- return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)];
- else
- return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)];
-}
+private:
+ void enable_ram(u8 data)
+ {
+ set_ram_enable(0x0a == (data & 0x0f));
+ }
-void gb_rom_mbc7_device::write_bank(offs_t offset, uint8_t data)
-{
- if (offset < 0x2000)
+ void bank_switch_fine(u8 data)
{
- // FIXME: Add RAM enable support
- logerror("%s Write to ram enable register 0x%04X <- 0x%02X\n", machine().describe_context(), offset, data);
+ data &= 0x1f;
+ set_bank_rom_fine(data ? data : 1);
}
- else if (offset < 0x3000)
+
+ void bank_low_mask(u8 data)
{
- logerror( "%s write to mbc7 rom select register: 0x%04X <- 0x%02X\n", machine().describe_context(), 0x2000 + offset, data );
- /* Bit 12 must be set for writing to the mbc register */
- if (offset & 0x0100)
- m_latch_bank2 = data;
+ u8 const mask(data ? 0x03 : 0x00);
+ LOG(
+ "%s: RAM/low ROM bank switching %s\n",
+ machine().describe_context(),
+ mask ? "enabled" : "disabled");
+ set_bank_rom_low_coarse_mask(mask);
+ set_bank_ram_mask(mask);
}
- else
+
+ bool is_collection() ATTR_COLD
{
- logerror( "%s write to mbc7 rom area: 0x%04X <- 0x%02X\n", machine().describe_context(), 0x3000 + offset, data );
- /* Bit 12 must be set for writing to the mbc register */
- if (offset & 0x0100)
+ // addressing must be explicitly specified in software lists
+ if (loaded_through_softlist())
+ return false;
+
+ // need ROM to probe
+ memory_region *const romregion(cart_rom_region());
+ if (!romregion)
+ return false;
+
+ // doesn't make sense without more than 256 KiB, and can address up to 1 MiB in 16 KiB pages
+ auto const rombytes(romregion->bytes());
+ if (((u32(0x4000) << 6) < rombytes) || ((u32(0x4000) << 4) >= rombytes) || (rombytes & 0x3fff))
+ return false;
+
+ // reject if the header checksum for the first page fails
+ u8 const *const rombase(romregion->base());
+ if (!cartheader::verify_header_checksum(rombase + 0x100))
+ return false;
+
+ // check title in first page header against list of known collections
+ static constexpr u8 KNOWN_COLLECTIONS[][0x10] = {
+ { 'B', 'O', 'M', 'C', 'O', 'L', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 'B', 'O', 'M', 'S', 'E', 'L', 0, 0, 0, 0, 0, 'B', '2', 'C', 'K', 0xc0 },
+ { 'G', 'E', 'N', 'C', 'O', 'L', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 'M', 'O', 'M', 'O', 'C', 'O', 'L', 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 'M', 'O', 'R', 'T', 'A', 'L', 'K', 'O', 'M', 'B', 'A', 'T', ' ', 'D', 'U', 'O' },
+ { 'M', 'O', 'R', 'T', 'A', 'L', 'K', 'O', 'M', 'B', 'A', 'T', 'I', '&', 'I', 'I' },
+ { 'S', 'U', 'P', 'E', 'R', 'C', 'H', 'I', 'N', 'E', 'S', 'E', ' ', '1', '2', '3' } };
+ auto const known(
+ std::find_if(
+ std::begin(KNOWN_COLLECTIONS),
+ std::end(KNOWN_COLLECTIONS),
+ [rombase] (auto const &name)
+ {
+ return std::equal(std::begin(name), std::end(name), &rombase[cartheader::OFFSET_TITLE]);
+ }));
+ if (std::end(KNOWN_COLLECTIONS) != known)
{
- switch (offset & 0x7000)
- {
- case 0x3000: /* 0x3000-0x3fff */
- case 0x4000: /* 0x4000-0x4fff */
- case 0x5000: /* 0x5000-0x5fff */
- case 0x6000: /* 0x6000-0x6fff */
- case 0x7000: /* 0x7000-0x7fff */
- break;
- }
+ logerror("Detected known multi-game collection with 4-bit fine bank addressing\n");
+ return true;
}
- }
-}
-uint8_t gb_rom_mbc7_device::read_ram(offs_t offset)
-{
- if (!m_ram.empty())
- return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)];
- else
- return 0xff;
-}
-
-void gb_rom_mbc7_device::write_ram(offs_t offset, uint8_t data)
-{
- if (!m_ram.empty())
- m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data;
-}
-
-
-// M161
-
-uint8_t gb_rom_m161_device::read_rom(offs_t offset)
-{
- return m_rom[rom_bank_map[m_base_bank] * 0x4000 + (offset & 0x7fff)];
-}
+ // reject if the header checksum for the second coarse page fails
+ if (!cartheader::verify_header_checksum(rombase + (u32(0x4000) << 4) + 0x100))
+ return false;
-void gb_rom_m161_device::write_bank(offs_t offset, uint8_t data)
-{
- // the mapper (74HC161A) only has data lines D2..D0
- data &= 0x07;
+ // assume it's a collection if the type indicates an MBC1 cartridge
+ switch (rombase[(u32(0x4000) << 4) + cartheader::OFFSET_TYPE])
+ {
+ case cartheader::TYPE_MBC1:
+ case cartheader::TYPE_MBC1_RAM:
+ case cartheader::TYPE_MBC1_RAM_BATT:
+ logerror("Detected MBC1 header in page 0x10, using 4-bit fine bank addressing\n");
+ return true;
+ }
- // A15 is connected to #LOAD and overwritten by QD (m_load_disable)
- switch (offset & 0x8000)
- {
- case 0x0000: // Base Bank Register
- if (!m_load_disable)
- m_base_bank = data << 1;
- m_load_disable = 0x01;
- break;
- default:
- break;
+ // assume anything else uses a different (presumably more conventional) scheme
+ return false;
}
-}
-
-
-// MMM01
-
-uint8_t gb_rom_mmm01_device::read_rom(offs_t offset)
-{
- uint16_t romb = m_romb & ~(0x1e0 | m_romb_nwe);
- uint16_t romb_base = m_romb & (0x1e0 | m_romb_nwe);
- uint8_t ramb_masked = ((offset & 0x4000) | m_mode ? m_ramb : m_ramb & ~0x03);
-
- // zero-adjust RA18..RA14
- romb = (romb ? romb : 0x01);
- // if unmapped, force
- romb = (m_map ? romb : 0x01);
-
- // RB 0 logic
- if (!(offset & 0x4000))
- romb = 0x00;
-
- // combine with base
- romb |= romb_base;
+};
- // multiplex with AA14..AA13
- if (m_mux)
- romb = (romb & ~0x60) | ((ramb_masked & 0x03) << 5);
- // if unmapped, force
- if (!m_map)
- romb |= 0x1fe;
- return m_rom[rom_bank_map[romb] * 0x4000 + (offset & 0x3fff)];
-}
+//**************************************************************************
+// MBC5 (max 128 MiB ROM, max 128 KiB SRAM)
+//**************************************************************************
-void gb_rom_mmm01_device::write_bank(offs_t offset, uint8_t data)
+class mbc5_device : public mbc5_device_base
{
- // the mapper only has data lines D6..D0
- data &= 0x7f;
+public:
+ mbc5_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ mbc5_device_base(mconfig, GB_ROM_MBC5, tag, owner, clock),
+ m_rumble(*this, "rumble"),
+ m_has_rumble(false)
+ {
+ }
- // the mapper only uses inputs A15..A13
- switch (offset & 0xe000)
+ virtual std::error_condition load(std::string &message) override ATTR_COLD
{
- case 0x0000: // Map Latch, AA #WE, RAM Enable
- if (!m_map) {
- m_ramb_nwe = (data & (0x30)) >> 4;
- m_map = (data & 0x40);
+ // check whether rumble motor is present
+ char const *const rumblefeature(get_feature("rumble"));
+ if (rumblefeature)
+ {
+ // explicitly specified in software list
+ if (util::streqlower(rumblefeature, "yes") || util::streqlower(rumblefeature, "true"))
+ {
+ logerror("Rumble motor present\n");
+ m_has_rumble = true;
}
- m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0;
- break;
- case 0x2000: // RA20..RA19 RA18..RA14
- if (!m_map)
- m_romb = (m_romb & ~0x60) | (data & 0x60);
-
- m_romb = (m_romb & (~0x1f | m_romb_nwe)) | (data & (0x1f & ~m_romb_nwe));
- break;
- case 0x4000: // Mode #WE, RA22..RA21, AA16..AA15, AA14..AA13
- if (!m_map) {
- m_mode_nwe = data & 0x40;
- m_romb = (m_romb & ~0x180) | ((data & 0x30) << 3);
- m_ramb = (m_ramb & ~0x0c) | (data & 0x0c);
+ else if (util::streqlower(rumblefeature, "no") || util::streqlower(rumblefeature, "false"))
+ {
+ logerror("No rumble motor present\n");
+ m_has_rumble = false;
}
-
- m_ramb = (m_ramb & (~0x03 | m_ramb_nwe)) | (data & (0x03 & ~m_ramb_nwe));
- break;
- case 0x6000: // Mux, RA18..RA15 #WE/Mask, ???, MBC1 Mode
- if (!m_map) {
- m_mux = data & 0x40;
- // m_romb_nwe is aligned to RA14, hence >> 1 instead of >> 2
- m_romb_nwe = (data & 0x3c) >> 1;
+ else
+ {
+ message = "Invalid 'rumble' feature value (must be yes or no)";
+ return image_error::BADSOFTWARE;
}
+ }
+ else if (loaded_through_softlist())
+ {
+ logerror("No 'rumble' feature found, assuming no rumble motor present\n");
+ m_has_rumble = false;
+ }
+ else
+ {
+ gbxfile::leader_1_0 leader;
+ u8 const *extra;
+ u32 extralen;
+ if (gbxfile::get_data(gbx_footer_region(), leader, extra, extralen))
+ {
+ m_has_rumble = bool(leader.rumble);
+ logerror("GBX format image specifies %srumble motor present\n", m_has_rumble ? "" : "no ");
+ }
+ else
+ {
+ // try probing the header
+ memory_region *const romregion(cart_rom_region());
+ if (romregion && (romregion->bytes() > cartheader::OFFSET_TYPE))
+ {
+ u8 const carttype((&romregion->as_u8())[cartheader::OFFSET_TYPE]);
+ switch (carttype)
+ {
+ case cartheader::TYPE_MBC5_RUMBLE:
+ case cartheader::TYPE_MBC5_RUMBLE_RAM:
+ case cartheader::TYPE_MBC5_RUMBLE_RAM_BATT:
+ m_has_rumble = true;
+ break;
+ case cartheader::TYPE_MBC5:
+ case cartheader::TYPE_MBC5_RAM:
+ case cartheader::TYPE_MBC5_RAM_BATT:
+ m_has_rumble = false;
+ break;
+ default:
+ osd_printf_warning(
+ "[%s] Unrecognized cartridge type 0x%02X in header, assuming no rumble motor present\n",
+ tag(),
+ carttype);
+ m_has_rumble = false;
+ }
+ logerror(
+ "Cartridge type 0x%02X in header, %srumble motor present\n",
+ carttype,
+ m_has_rumble ? "" : "no ");
+ }
+ }
+ }
- if (!m_mode_nwe)
- m_mode = data & 0x01;
- break;
- default:
- break;
- }
-}
-
-uint8_t gb_rom_mmm01_device::read_ram(offs_t offset)
-{
- uint8_t ramb_masked = ((offset & 0x4000) | m_mode ? m_ramb : m_ramb & ~0x03);
- uint8_t ramb = ramb_masked;
+ // install base MBC5 handlers
+ std::error_condition const result(mbc5_device_base::load(message));
+ if (result)
+ return result;
- // multiplex with RA20..RA19
- if (m_mux)
- ramb = (ramb & ~0x03) | ((m_romb & 0x60) >> 5);
+ // install rumble-aware handler if appropriate
+ if (m_has_rumble)
+ {
+ cart_space()->install_write_handler(
+ 0x4000, 0x5fff,
+ emu::rw_delegate(*this, FUNC(mbc5_device::bank_switch_coarse)));
+ }
- if (!m_ram.empty() && m_ram_enable)
- {
- return m_ram[ram_bank_map[ramb] * 0x2000 + (offset & 0x1fff)];
+ // all good
+ return std::error_condition();
}
- else
- return 0xff;
-}
-void gb_rom_mmm01_device::write_ram(offs_t offset, uint8_t data)
-{
- uint8_t ramb_masked = ((offset & 0x4000) | m_mode ? m_ramb : m_ramb & ~0x03);
- uint8_t ramb = ramb_masked;
-
- // multiplex with RA20..RA19
- if (m_mux)
- ramb = (ramb & ~0x03) | ((m_romb & 0x60) >> 5);
-
- if (!m_ram.empty() && m_ram_enable)
+protected:
+ virtual void device_start() override ATTR_COLD
{
- m_ram[ram_bank_map[ramb] * 0x2000 + (offset & 0x1fff)] = data;
- }
-}
+ mbc5_device_base::device_start();
-// Sachen MMC1
+ m_rumble.resolve();
+ }
-uint8_t gb_rom_sachen_mmc1_device::read_rom(offs_t offset)
-{
- uint16_t off_edit = offset;
-
- /* Wait for 0x31 transitions of A15 (hi -> lo), i.e. ROM accesses; A15 = HI while in bootstrap */
- /* This is 0x31 transitions, because we increment counter _after_ checking it */
- if (m_unlock_cnt == 0x30)
- m_mode = MODE_UNLOCKED;
- else
- m_unlock_cnt++;
-
- /* Logo Switch */
- if (m_mode == MODE_LOCKED)
- off_edit |= 0x80;
-
- /* Header Un-Scramble */
- if ((off_edit & 0xff00) == 0x0100) {
- off_edit &= 0xffac;
- off_edit |= ((offset >> 6) & 0x01) << 0;
- off_edit |= ((offset >> 4) & 0x01) << 1;
- off_edit |= ((offset >> 1) & 0x01) << 4;
- off_edit |= ((offset >> 0) & 0x01) << 6;
- }
- //logerror("read from %04X (%04X)\n", offset, off_edit);
-
- if (offset & 0x4000) /* RB1 */
- return m_rom[rom_bank_map[(m_base_bank & m_mask) | (m_latch_bank2 & ~m_mask)] * 0x4000 + (offset & 0x3fff)];
- else /* RB0 */
- return m_rom[rom_bank_map[(m_base_bank & m_mask) | (m_latch_bank & ~m_mask)] * 0x4000 + (off_edit & 0x3fff)];
-}
-
-void gb_rom_sachen_mmc1_device::write_bank(offs_t offset, uint8_t data)
-{
- /* Only A15..A6, A4, A1..A0 are connected */
- /* We only decode upper three bits */
- switch ((offset & 0xffd3) & 0xe000)
+ virtual void device_reset() override ATTR_COLD
{
- case 0x0000: /* Base ROM Bank Register */
+ mbc5_device_base::device_reset();
- if ((m_latch_bank2 & 0x30) == 0x30)
- m_base_bank = data;
- //logerror("write to base bank %X - %X\n", data, (m_base_bank & m_mask) | (m_latch_bank2 & ~m_mask));
- break;
-
- case 0x2000: /* ROM Bank Register */
-
- m_latch_bank2 = data ? data : 0x01;
- //logerror("write to latch %X - %X\n", data, (m_base_bank & m_mask) | (m_latch_bank2 & ~m_mask));
- break;
+ if (m_has_rumble)
+ m_rumble = 0;
+ }
- case 0x4000: /* ROM Bank Mask Register */
+private:
+ void bank_switch_coarse(u8 data)
+ {
+ assert(m_has_rumble);
+ mbc5_device_base::bank_switch_coarse(data);
+ m_rumble = BIT(data, 3);
+ }
- if ((m_latch_bank2 & 0x30) == 0x30)
- m_mask = data;
- //logerror("write to mask %X - %X\n", data, (m_base_bank & m_mask) | (m_latch_bank2 & ~m_mask));
- break;
+ output_finder<> m_rumble;
+ bool m_has_rumble;
+};
- case 0x6000:
- /* nothing happens when writing to 0x6000-0x7fff, as verified by Tauwasser */
- break;
- default:
+//**************************************************************************
+// MBC5 variant used by BBD games
+//**************************************************************************
- //logerror("write to unknown/unmapped area %04X <= %02X\n", offset, data);
- /* did not extensively test other unlikely ranges */
- break;
+class bbd_device : public mbc5_scrambled_device_base
+{
+public:
+ bbd_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ mbc5_scrambled_device_base(mconfig, GB_ROM_BBD, tag, owner, clock)
+ {
}
-}
-// Sachen MMC2
+protected:
+ virtual unscramble_function unscramble_data(u8 data) override
+ {
+ switch (data & 0x07)
+ {
+ case 0x0: // no scrambling
+ return &bbd_device::unscramble<7, 6, 5, 4, 3, 2, 1, 0>;
+ case 0x4: // Garou
+ return &bbd_device::unscramble<7, 6, 2, 4, 3, 1, 5, 0>;
+ case 0x5: // Harry Potter
+ return &bbd_device::unscramble<7, 6, 5, 1, 3, 2, 4, 0>;
+ case 0x7: // Digimon Adventure
+ return &bbd_device::unscramble<7, 6, 2, 4, 3, 5, 1, 0>;
+ }
-uint8_t gb_rom_sachen_mmc2_device::read_rom(offs_t offset)
-{
- uint16_t off_edit = offset;
+ logerror(
+ "%s: Unsupported ROM data scrambling mode 0x%02X\n",
+ machine().describe_context(),
+ data);
+ return &bbd_device::unscramble<7, 6, 5, 4, 3, 2, 1, 0>;
+ }
- /* Wait for 0x30 transitions of A15 (lo -> hi), i.e. ROM accesses; A15 = HI while in bootstrap */
- /* This is 0x30 transitions, because we increment counter _after_ checking it, but A15 lo -> hi*/
- /* transition means first read (hi -> lo transition) must not count */
+ virtual unscramble_function unscramble_bank(u8 data) override
+ {
+ switch (data & 0x07)
+ {
+ case 0x0: // no scrambling
+ return &bbd_device::unscramble<7, 6, 5, 4, 3, 2, 1, 0>;
+ case 0x3: // Digimon Adventure, Garou - two most significant bits unconfirmed
+ return &bbd_device::unscramble<7, 6, 5, 1, 0, 2, 4, 3>;
+ case 0x5: // Harry Potter - two most significant bits unconfirmed
+ return &bbd_device::unscramble<7, 6, 5, 0, 4, 3, 2, 1>;
+ }
- if (m_unlock_cnt == 0x30 && m_mode == MODE_LOCKED_DMG) {
- m_mode = MODE_LOCKED_CGB;
- m_unlock_cnt = 0x00;
- } else if (m_unlock_cnt == 0x30 && m_mode == MODE_LOCKED_CGB) {
- m_mode = MODE_UNLOCKED;
+ logerror(
+ "%s: Unsupported ROM bank number scrambling mode 0x%02X\n",
+ machine().describe_context(),
+ data);
+ return &bbd_device::unscramble<7, 6, 5, 4, 3, 2, 1, 0>;
}
+};
- if (m_unlock_cnt != 0x30)
- m_unlock_cnt++;
- /* Logo Switch */
- if (m_mode == MODE_LOCKED_CGB)
- off_edit |= 0x80;
- /* Header Un-Scramble */
- if ((off_edit & 0xff00) == 0x0100) {
- off_edit &= 0xffac;
- off_edit |= ((offset >> 6) & 0x01) << 0;
- off_edit |= ((offset >> 4) & 0x01) << 1;
- off_edit |= ((offset >> 1) & 0x01) << 4;
- off_edit |= ((offset >> 0) & 0x01) << 6;
- }
- //logerror("read from %04X (%04X) cnt: %02X\n", offset, off_edit, m_unlock_cnt);
+//**************************************************************************
+// MBC5 variant with DSHGGB-81 PCB
+//**************************************************************************
- if (offset & 0x4000) /* RB1 */
- return m_rom[rom_bank_map[(m_base_bank & m_mask) | (m_latch_bank2 & ~m_mask)] * 0x4000 + (offset & 0x3fff)];
- else /* RB0 */
- return m_rom[rom_bank_map[(m_base_bank & m_mask) | (m_latch_bank & ~m_mask)] * 0x4000 + (off_edit & 0x3fff)];
-}
-
-uint8_t gb_rom_sachen_mmc2_device::read_ram(offs_t offset)
+class dshggb81_device : public mbc5_scrambled_device_base
{
- if (m_mode == MODE_LOCKED_DMG) {
- m_unlock_cnt = 0x00;
- m_mode = MODE_LOCKED_CGB;
+public:
+ dshggb81_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ mbc5_scrambled_device_base(mconfig, GB_ROM_DSHGGB81, tag, owner, clock)
+ {
}
- return 0xff;
-
-}
-void gb_rom_sachen_mmc2_device::write_ram(offs_t offset, uint8_t data)
-{
- if (m_mode == MODE_LOCKED_DMG) {
- m_unlock_cnt = 0x00;
- m_mode = MODE_LOCKED_CGB;
+protected:
+ virtual unscramble_function unscramble_data(u8 data) override
+ {
+ switch (data & 0x07)
+ {
+ default: // just to shut up dumb compilers
+ case 0x0: return &dshggb81_device::unscramble<7, 6, 5, 4, 3, 2, 1, 0>;
+ case 0x1: return &dshggb81_device::unscramble<7, 5, 6, 4, 3, 1, 2, 0>;
+ case 0x2: return &dshggb81_device::unscramble<7, 1, 2, 4, 3, 5, 6, 0>;
+ case 0x3: return &dshggb81_device::unscramble<7, 6, 2, 4, 3, 1, 5, 0>;
+ case 0x4: return &dshggb81_device::unscramble<7, 6, 1, 4, 3, 2, 5, 0>;
+ case 0x5: return &dshggb81_device::unscramble<7, 1, 5, 4, 3, 6, 2, 0>;
+ case 0x6: return &dshggb81_device::unscramble<7, 5, 2, 4, 3, 6, 1, 0>;
+ case 0x7: return &dshggb81_device::unscramble<7, 1, 6, 4, 3, 5, 2, 0>;
+ }
}
-}
+ virtual unscramble_function unscramble_bank(u8 data) override
+ {
+ switch (data & 0x07)
+ {
+ case 0x0: // no scrambling
+ return &dshggb81_device::unscramble<7, 6, 5, 4, 3, 2, 1, 0>;
+ }
+
+ logerror(
+ "%s: Unsupported ROM bank number scrambling mode 0x%02X\n",
+ machine().describe_context(),
+ data);
+ return &dshggb81_device::unscramble<7, 6, 5, 4, 3, 2, 1, 0>;
+ }
+};
-// 188 in 1 pirate (only preliminary)
-uint8_t gb_rom_188in1_device::read_rom(offs_t offset)
-{
- if (offset < 0x4000)
- return m_rom[m_game_base + rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)];
- else
- return m_rom[m_game_base + rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)];
-}
+//**************************************************************************
+// MBC5 variant used by Sintax games
+//**************************************************************************
-void gb_rom_188in1_device::write_bank(offs_t offset, uint8_t data)
+class sintax_device : public mbc5_logo_spoof_device_base
{
- if (offset == 0x7b00)
+public:
+ sintax_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ mbc5_logo_spoof_device_base(mconfig, GB_ROM_SINTAX, tag, owner, clock),
+ m_scramble_mode(0U),
+ m_xor_index(0U)
{
- if (data < 0x80)
- logerror("write to 0x%X data 0x%X\n", offset, data);
- else
- {
- data -= 0x80;
- m_game_base = 0x400000 + (data * 0x8000);
- //logerror("offset 0x%X\n", m_game_base);
- }
}
- else if (offset == 0x7b01 || offset == 0x7b02)
+
+ virtual std::error_condition load(std::string &message) override ATTR_COLD
{
- // what do these writes do?
- printf("write to 0x%X data 0x%X\n", offset, data);
+ // install regular MBC5 handlers
+ std::error_condition const result(mbc5_logo_spoof_device_base::load(message));
+ if (result)
+ return result;
+
+ // bank numbers and ROM contents are scrambled for protection
+ cart_space()->install_read_handler(
+ 0x0000, 0x3fff,
+ emu::rw_delegate(*this, FUNC(sintax_device::read_rom_low)));
+ cart_space()->install_read_handler(
+ 0x4000, 0x7fff,
+ emu::rw_delegate(*this, FUNC(sintax_device::read_rom_high)));
+ cart_space()->install_write_handler(
+ 0x2000, 0x2fff,
+ emu::rw_delegate(*this, FUNC(sintax_device::bank_switch_fine_low_scrambled)));
+ cart_space()->install_write_handler(
+ 0x5000, 0x5fff,
+ emu::rw_delegate(*this, FUNC(sintax_device::set_scramble_mode)));
+ cart_space()->install_write_handler(
+ 0x7000, 0x70ff, 0x0000, 0x0f00, 0x0000,
+ emu::rw_delegate(*this, FUNC(sintax_device::set_xor)));
+
+ // all good
+ return std::error_condition();
}
- else
- gb_rom_mbc1_device::write_bank(offset, data);
-}
+protected:
+ virtual void device_start() override ATTR_COLD
+ {
+ mbc5_logo_spoof_device_base::device_start();
-// MBC5 variant used by Li Cheng / Niutoude games
-
-void gb_rom_licheng_device::write_bank(offs_t offset, uint8_t data)
-{
- if (offset > 0x2100 && offset < 0x3000)
- return;
+ save_item(NAME(m_scramble_mode));
+ save_item(NAME(m_xor_index));
+ save_item(NAME(m_xor_table));
+ }
- gb_rom_mbc5_device::write_bank(offset, data);
-}
+ virtual void device_reset() override ATTR_COLD
+ {
+ mbc5_logo_spoof_device_base::device_reset();
-// MBC5 variant used by Chong Wu Xiao Jing Ling (this appears to be a re-release of a Li Cheng / Niutoude game,
-// given that it contains the Niutoude logo, with most protection checks patched out)
+ m_scramble_mode = 0U;
+ m_xor_index = 0U;
+ std::fill(std::begin(m_xor_table), std::end(m_xor_table), 0U);
+ }
-uint8_t gb_rom_chongwu_device::read_rom(offs_t offset)
-{
- // protection check at the first read here...
- if (offset == 0x41c3 && !m_protection_checked)
+private:
+ u8 read_rom_low(offs_t offset)
{
- m_protection_checked = 1;
- return 0x5d;
+ offset = rom_access(offset);
+ return bank_rom_low_base()[offset];
}
- if (offset < 0x4000)
- return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)];
- else
- return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)];
-}
-
-// MBC5 variant used by Sintax games
+ u8 read_rom_high(offs_t offset)
+ {
+ offset = rom_access(0x4000 | offset) & 0x3fff;
+ return bank_rom_high_base()[offset] ^ m_xor_table[m_xor_index];
+ }
-void gb_rom_sintax_device::set_xor_for_bank(uint8_t bank)
-{
- switch (bank & 0x0f)
+ void bank_switch_fine_low_scrambled(u8 data)
{
- case 0x00: case 0x04: case 0x08: case 0x0c:
- m_currentxor = m_xor2;
+ m_xor_index = data & 0x03;
+
+ switch (m_scramble_mode & 0x0f)
+ {
+ case 0x00: // Lion King, Golden Sun
+ data = bitswap<8>(data, 7, 0, 5, 6, 3, 4, 1, 2);
break;
- case 0x01: case 0x05: case 0x09: case 0x0d:
- m_currentxor = m_xor3;
+ case 0x01: // Langrisser
+ data = bitswap<8>(data, 0, 1, 6, 7, 4, 5, 2, 3);
break;
- case 0x02: case 0x06: case 0x0a: case 0x0e:
- m_currentxor = m_xor4;
+ case 0x05: // Maple Story, Pokemon Platinum
+ data = bitswap<8>(data, 7, 6, 1, 0, 3, 2, 5, 4); // needs verification
break;
- case 0x03: case 0x07: case 0x0b: case 0x0f:
- m_currentxor = m_xor5;
+ case 0x07: // Bynasty Warriors 5
+ data = bitswap<8>(data, 2, 0, 3, 1, 5, 4, 7, 6); // 5 and 7 unconfirmed
break;
- }
-}
+ case 0x09:
+ data = bitswap<8>(data, 4, 5, 2, 3, 0, 1, 6, 7);
+ break;
+ case 0x0b: // Shaolin Legend
+ data = bitswap<8>(data, 2, 3, 0, 1, 6, 7, 4, 5); // 5 and 6 unconfirmed
+ break;
+ case 0x0d: // older games
+ data = bitswap<8>(data, 1, 0, 7, 6, 5, 4, 3, 2);
+ break;
+ case 0x0f: // no scrambling
+ break;
+ default:
+ logerror(
+ "%s: Unsupported ROM bank number scrambling mode 0x%X\n",
+ machine().describe_context(),
+ m_scramble_mode & 0x0f);
+ }
-uint8_t gb_rom_sintax_device::read_rom(offs_t offset)
-{
- if (offset < 0x4000)
- return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)];
- else
- return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)] ^ m_currentxor;
-}
+ set_bank_rom_fine((bank_rom_fine() & 0x0100) | u16(data));
+ }
-void gb_rom_sintax_device::write_bank(offs_t offset, uint8_t data)
-{
- if (offset < 0x2000)
- m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0;
- else if (offset < 0x3000)
+ void set_scramble_mode(u8 data)
{
- set_xor_for_bank(data);
-
- switch (m_sintax_mode & 0x0f)
+ if (!m_scramble_mode)
{
- case 0x0d:
- data = bitswap<8>(data, 1,0,7,6,5,4,3,2);
- break;
- case 0x09:
- //data = bitswap<8>(data, 3,2,5,4,0,1,6,7); // Monkey..no
- data = bitswap<8>(data, 4,5,2,3,0,1,6,7);
- break;
- case 0x00: // 0x10=lion 0x00 hmmmmm // 1 and 0 unconfirmed
- data = bitswap<8>(data, 7,0,5,6,3,4,1,2);
- break;
- case 0x01:
- data = bitswap<8>(data, 0,1,6,7,4,5,2,3);
- break;
- case 0x05:
- data = bitswap<8>(data, 7,6,1,0,3,2,5,4); // Not 100% on this one
- break;
- case 0x07:
- data = bitswap<8>(data, 2,0,3,1,5,4,7,6); // 5 and 7 unconfirmed
- break;
- case 0x0b:
- data = bitswap<8>(data, 2,3,0,1,6,7,4,5); // 5 and 6 unconfirmed
- break;
+ LOG(
+ "%s: Set scramble mode = 0x%02X\n",
+ machine().describe_context(),
+ data);
+ m_scramble_mode = data;
+ bank_switch_fine_low_scrambled(0x01);
}
- m_latch_bank2 = (m_latch_bank2 & 0x100) | data;
- }
- else if (offset < 0x4000)
- {
- m_latch_bank2 = (m_latch_bank2 & 0xff) | ((data & 0x01) << 8);
- }
- else if (offset < 0x5000)
- {
- data &= 0x0f;
- m_ram_bank = data;
- }
- else if (offset < 0x6000)
- {
- if (!m_sintax_mode)
+ else
{
- m_sintax_mode = data;
- write_bank(0x2000, 1); //force a fake bank switch
+ LOG(
+ "%s: Ignoring scramble mode = 0x%02X when already set to 0x%02X\n",
+ machine().describe_context(),
+ data,
+ m_scramble_mode);
}
-// printf("sintax mode %x\n", m_sintax_mode & 0xf);
}
- else if (offset >= 0x7000)
+
+ void set_xor(offs_t offset, u8 data)
{
- switch ((offset & 0x00f0) >> 4)
+ u8 const index(offset >> 4);
+ if ((2 <= index) && (6 > index))
{
- case 2:
- m_xor2 = data;
- break;
- case 3:
- m_xor3 = data;
- break;
- case 4:
- m_xor4 = data;
- break;
- case 5:
- m_xor5 = data;
- break;
+ LOG(
+ "%s: Set XOR entry %u = 0x%02X\n",
+ machine().describe_context(),
+ index - 2,
+ data);
+ m_xor_table[index - 2] = data;
}
-
- if (m_currentxor == 0)
- set_xor_for_bank(4);
}
-}
-
-uint8_t gb_rom_sintax_device::read_ram(offs_t offset)
-{
- if (!m_ram.empty() && m_ram_enable)
- return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)];
- else
- return 0xff;
-}
-
-void gb_rom_sintax_device::write_ram(offs_t offset, uint8_t data)
-{
- if (!m_ram.empty() && m_ram_enable)
- m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data;
-}
-
-/*
-
- Further MBC5 variants to emulate:
-
- Digimon 2 & Digimon 4 (Yong Yong)
-
- Digimon 2 writes at $2000 to select latch2 (data must be divided by 2, and 0 becomes 1),
- then writes to $2400 a series of values that the patched version does not write...
- Digimon 4 seems to share part of the $2000 behavior, but does not write to $2400...
+ u8 m_scramble_mode;
+ u8 m_xor_index;
+ u8 m_xor_table[4];
+};
- */
-// MBC5 variant used by Digimon 2 (and maybe 4?)
-uint8_t gb_rom_digimon_device::read_rom(offs_t offset)
-{
- if (offset < 0x4000)
- return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)];
- else
- return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)];
-}
+//**************************************************************************
+// MBC5 variant used by Chǒngwù Xiǎo Jīnglíng - Jié Jīn Tǎ Zhī Wáng
+//**************************************************************************
-void gb_rom_digimon_device::write_bank(offs_t offset, uint8_t data)
+class chongwu_device : public mbc5_logo_spoof_device_base
{
- if (offset < 0x2000)
- m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0;
- else if (offset == 0x2000)
+public:
+ chongwu_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ mbc5_logo_spoof_device_base(mconfig, GB_ROM_CHONGWU, tag, owner, clock),
+ m_protection_checked(0U),
+ m_installing_tap(false)
{
-// printf("written $02 %X at %X\n", data, offset);
- if (!data)
- data++;
- m_latch_bank2 = data/2;
}
- else if (offset < 0x3000)
+
+protected:
+ virtual void device_start() override ATTR_COLD
{
-// printf("written $03 %X at %X\n", data, offset);
+ mbc5_logo_spoof_device_base::device_start();
+
+ save_item(NAME(m_protection_checked));
}
- else if (offset < 0x4000)
+
+ virtual void device_reset() override ATTR_COLD
{
-// printf("written $04 %X at %X\n", data, offset);
+ mbc5_logo_spoof_device_base::device_reset();
+
+ m_protection_checked = 0U;
+
+ install_protection_tap();
+ m_notif_cart_space = cart_space()->add_change_notifier(
+ [this] (read_or_write mode)
+ {
+ if (u32(mode) & u32(read_or_write::READ))
+ install_protection_tap();
+ });
}
- else if (offset < 0x6000)
+
+private:
+ void install_protection_tap()
{
-// printf("written $05-$06 %X at %X\n", data, offset);
- data &= 0x0f;
- m_ram_bank = data;
+ if (!m_installing_tap)
+ {
+ m_installing_tap = true;
+ m_protection_tap.remove();
+ if (!m_protection_checked)
+ {
+ m_protection_tap = cart_space()->install_read_tap(
+ 0x41c3, 0x41c3,
+ "protection_tap_r",
+ [this] (offs_t offset, u8 &data, u8 mem_mask)
+ {
+ assert(!m_protection_checked);
+
+ m_protection_checked = 1U;
+ m_notif_cart_space.reset();
+ m_protection_tap.remove();
+
+ data = 0x5d;
+ },
+ &m_protection_tap);
+ }
+ else
+ {
+ m_notif_cart_space.reset();
+ }
+ m_installing_tap = false;
+ }
}
-// else
-// printf("written $07 %X at %X\n", data, offset);
-}
-uint8_t gb_rom_digimon_device::read_ram(offs_t offset)
-{
- if (!m_ram.empty() && m_ram_enable)
- return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)];
- else
- return 0xff;
-}
+ memory_passthrough_handler m_protection_tap;
+ util::notifier_subscription m_notif_cart_space;
-void gb_rom_digimon_device::write_ram(offs_t offset, uint8_t data)
-{
- if (!m_ram.empty() && m_ram_enable)
- m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data;
-}
+ u8 m_protection_checked;
+ bool m_installing_tap;
+};
-// MBC1 variant used by Yong Yong for Rockman 8
-uint8_t gb_rom_rockman8_device::read_rom(offs_t offset)
-{
- if (offset < 0x4000)
- return m_rom[m_latch_bank * 0x4000 + (offset & 0x3fff)];
- else
- return m_rom[m_latch_bank2 * 0x4000 + (offset & 0x3fff)];
-}
+//**************************************************************************
+// MBC5 variant used by Li Cheng/Niutoude games
+//**************************************************************************
-void gb_rom_rockman8_device::write_bank(offs_t offset, uint8_t data)
+class licheng_device : public mbc5_logo_spoof_device_base
{
- if (offset < 0x2000)
- return;
- else if (offset < 0x4000)
+public:
+ licheng_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ mbc5_logo_spoof_device_base(mconfig, GB_ROM_LICHENG, tag, owner, clock)
{
- // 5bits only
- data &= 0x1f;
- if (data == 0)
- data = 1;
- if (data > 0xf)
- data -= 8;
+ }
- m_latch_bank2 = data;
+ virtual std::error_condition load(std::string &message) override ATTR_COLD
+ {
+ // install regular MBC5 handlers
+ std::error_condition const result(mbc5_logo_spoof_device_base::load(message));
+ if (result)
+ return result;
+
+ // protection against using a standard MBC5 - actual ignored range uncertain
+ // 0x2100 must not be ignored for Cannon Fodder sound
+ // 0x2180 must be ignored for FF DX3
+ cart_space()->unmap_write(0x2101, 0x2fff);
+
+ // all good
+ return std::error_condition();
}
-}
+};
-uint8_t gb_rom_rockman8_device::read_ram(offs_t offset)
-{
- if (!m_ram.empty())
- return m_ram[offset];
- else
- return 0xff;
-}
-void gb_rom_rockman8_device::write_ram(offs_t offset, uint8_t data)
-{
- if (!m_ram.empty())
- m_ram[offset] = data;
-}
-
-// MBC1 variant used by Yong Yong for Super Mario 3 Special
-
-// Mario special seems to be 512k image (mirrored up to 1m or 2m [redump needed to establish this])
-// it consists of 13 unique 16k chunks layed out as follows
-// unique chunk --> bank in bin
-// 1st to 7th --> 0x00 to 0x06
-// 8th --> 0x08
-// 9th --> 0x0b
-// 10th --> 0x0c
-// 11th --> 0x0d
-// 12th --> 0x0f
-// 13th --> 0x13
-
-// writing data to 0x2000-0x2fff switches bank according to the table below
-// (the value values corresponding to table[0x0f] is not confirmed, choices
-// 0,1,2,3,8,c,f freeze the game, while 4,5,6,7,b,d,0x13 work with glitches)
-static uint8_t smb3_table1[0x20] =
-{
- 0x00,0x04,0x01,0x05, 0x02,0x06,0x03,0x05, 0x08,0x0c,0x03,0x0d, 0x03,0x0b,0x0b,0x08 /* original doc here put 0x0f (i.e. 11th unique bank) */,
- 0x05,0x06,0x0b,0x0d, 0x08,0x06,0x13,0x0b, 0x08,0x05,0x05,0x08, 0x0b,0x0d,0x06,0x05
-};
-// according to old doc from Brian Provinciano, writing bit5 in 0x5000-0x5fff should
-// change the bank layout, in the sense that writing to bankswitch acts like if
-// the original rom has a different layout (as if unique chunks were under permutations
-// (24), (365) and (8a9) with 0,1,7,b,c fixed) and the same table above is used
-// however, no such a write ever happen (only bit4 is written, but changing mode with
-// bit4 breaks the gfx...)
+//**************************************************************************
+// New Game Boy Color cartridges with HK0701 and HK0819 boards
+//**************************************************************************
-uint8_t gb_rom_sm3sp_device::read_rom(offs_t offset)
+class ngbchk_device : public rom_mbc_device_base
{
- if (offset < 0x4000)
- return m_rom[rom_bank_map[0] * 0x4000 + (offset & 0x3fff)];
- else
- return m_rom[m_latch_bank2 * 0x4000 + (offset & 0x3fff)];
-}
+public:
+ ngbchk_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ rom_mbc_device_base(mconfig, GB_ROM_NEWGBCHK, tag, owner, clock),
+ m_view_prot(*this, "protection")
+ {
+ }
-void gb_rom_sm3sp_device::write_bank(offs_t offset, uint8_t data)
-{
-// printf("write 0x%x at %x\n", data, offset);
- if (offset < 0x2000)
- return;
- else if (offset < 0x3000)
- {
- // Table 1 confirmed...
- // 0->0, 4->2, 6->3
- // 1e -> 6 (level 1 bg gfx)
- // 19 -> 5 (level 2 bg gfx)
- // 1b -> 8 (level 3 bg gfx)
- // 1d -> D (level 4 bg gfx)
- // 1c -> B (bonus house bg gfx)
- // 1 (9 maybe, or 3)? f (5 maybe)? 2->1?
- // 16 -> 4-8? b?
-
- // 5bits only
- data &= 0x1f;
+ virtual std::error_condition load(std::string &message) override ATTR_COLD
+ {
+ // TODO: ROM addresses seem to be fully decoded with HK0819 (rather than mirroring)
+
+ // set up ROM and RAM
+ // TODO: how many RAM bank outputs are actually present?
+ std::error_condition err = install_memory(message, 2, 7);
+ if (err)
+ return err;
+
+ // install handlers
+ cart_space()->install_write_handler(
+ 0x0000, 0x1fff,
+ emu::rw_delegate(*this, FUNC(ngbchk_device::enable_ram)));
+ cart_space()->install_write_handler(
+ 0x2000, 0x3fff,
+ emu::rw_delegate(*this, FUNC(ngbchk_device::bank_switch_fine)));
+ cart_space()->install_write_handler(
+ 0x4000, 0x5fff,
+ emu::rw_delegate(*this, FUNC(ngbchk_device::bank_switch_coarse)));
+
+ // install protection over the top of high ROM bank
+ cart_space()->install_view(
+ 0x4000, 0x7fff,
+ m_view_prot);
+ m_view_prot[0].install_read_handler(
+ 0x4000, 0x4fff, 0x0ff0, 0x0000, 0x0000,
+ emu::rw_delegate(*this, FUNC(ngbchk_device::protection)));
+ m_view_prot[0].unmap_read(0x5000, 0x7fff);
+
+ // all good
+ return std::error_condition();
+ }
- m_latch_bank2 = smb3_table1[data];
- if (m_mode)
- {
- switch (m_latch_bank2)
- {
- case 0x02: m_latch_bank2 = 4; break;
- case 0x03: m_latch_bank2 = 6; break;
- case 0x04: m_latch_bank2 = 2; break;
- case 0x05: m_latch_bank2 = 3; break;
- case 0x06: m_latch_bank2 = 5; break;
- case 0x0b: m_latch_bank2 = 0xd; break;
- case 0x0c: m_latch_bank2 = 0xb; break;
- case 0x0d: m_latch_bank2 = 0xc; break;
-
- case 0x00:
- case 0x01:
- case 0x08:
- case 0x0f:
- case 0x13:
- default:
- break;
- }
- }
+protected:
+ virtual void device_reset() override ATTR_COLD
+ {
+ rom_mbc_device_base::device_reset();
+
+ // TODO: proper reset state
+ set_bank_rom_coarse(0);
+ set_bank_rom_fine(0);
+ set_ram_enable(false);
+
+ m_view_prot.disable();
}
- else if (offset < 0x5000)
+
+private:
+ void enable_ram(u8 data)
{
-// printf("write $5 %X at %X\n", data, offset);
- //maybe rumble??
+ // TODO: how many bits are checked?
+ set_ram_enable(0x0a == data);
}
- else if (offset < 0x6000)
+
+ void bank_switch_fine(u8 data)
{
-// printf("write mode %x\n", data);
- m_mode = BIT(data, 5);
-// write_bank(0x2000, 1);
+ set_bank_rom_fine(data & 0x7f);
+ LOG(
+ "%s: Protection read %s\n",
+ machine().describe_context(),
+ BIT(data, 7) ? "enabled" : "disabled");
+ if (BIT(data, 7))
+ m_view_prot.select(0);
+ else
+ m_view_prot.disable();
}
-}
-uint8_t gb_rom_sm3sp_device::read_ram(offs_t offset)
-{
- if (!m_ram.empty())
- return m_ram[offset];
- else
- return 0xff;
-}
+ u8 protection(offs_t offset)
+ {
+ offset >>= 4;
+ switch (offset & 0x007)
+ {
+ default: // just to shut up dumb compilers
+ case 0x000:
+ return offset;
+ case 0x001:
+ return offset ^ 0xaa;
+ case 0x002:
+ return offset ^ 0x55;
+ case 0x003:
+ return (offset >> 1) | (offset << 7);
+ case 0x004:
+ return (offset << 1) | (offset >> 7);
+ case 0x05:
+ return bitswap<8>(offset, 0, 1, 2, 3, 4, 5, 6, 7);
+ case 0x06:
+ return
+ (bitswap<4>(offset | (offset >> 1), 6, 4, 2, 0) << 4) |
+ bitswap<4>(offset & (offset >> 1), 6, 4, 2, 0);
+ case 0x07:
+ return
+ (bitswap<4>(offset ^ (~offset >> 1), 6, 4, 2, 0) << 4) |
+ bitswap<4>(offset ^ (offset >> 1), 6, 4, 2, 0);
+ }
+ }
-void gb_rom_sm3sp_device::write_ram(offs_t offset, uint8_t data)
-{
- if (!m_ram.empty())
- m_ram[offset] = data;
-}
+ memory_view m_view_prot;
+};
-void gb_rom_camera_device::update_camera()
-{
- m_camera_regs[0] &= ~0x1;
-}
-uint8_t gb_rom_camera_device::read_rom(offs_t offset)
-{
- if (offset < 0x4000)
- return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)];
- else
- return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)];
-}
-void gb_rom_camera_device::write_bank(offs_t offset, uint8_t data)
-{
- if (offset < 0x2000)
- m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0;
- else if (offset < 0x4000)
+//**************************************************************************
+// Fast Fame VF001 MBC5 variant with protection
+//**************************************************************************
+
+class vf001_device : public mbc5_logo_spoof_device_base
+{
+public:
+ vf001_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
+ mbc5_logo_spoof_device_base(mconfig, GB_ROM_VF001, tag, owner, clock),
+ m_bank_rom_upper(*this, "upper"),
+ m_bank_mask_rom_upper(0U),
+ m_command_preload(0U),
+ m_prot_cmd_enable(0U),
+ m_prot_cmd_data(0U),
+ m_readback_address(0U),
+ m_readback_bank(0U),
+ m_readback_length(0U),
+ m_readback_data{ 0U, 0U, 0U, 0U },
+ m_readback_count(0U),
+ m_split_address(0U),
+ m_split_enable(0U)
{
- // 7bits
- data &= 0x7f;
- /* Selecting bank 0 == selecting bank 1 */
- if (data == 0)
- data = 1;
+ }
+
+ virtual std::error_condition load(std::string &message) override ATTR_COLD
+ {
+ // get the command preload value
+ // TODO: add a way to specify this in software lists
+ gbxfile::leader_1_0 leader;
+ u8 const *extra;
+ u32 extralen;
+ if (gbxfile::get_data(gbx_footer_region(), leader, extra, extralen))
+ {
+ if (1 <= extralen)
+ {
+ m_command_preload = extra[0];
+ logerror(
+ "GBX format image specifies command preload value 0x%02X\n",
+ m_command_preload);
+ }
+ }
- m_latch_bank2 = data;
+ // install regular MBC5 handlers
+ std::error_condition const result(mbc5_logo_spoof_device_base::load(message));
+ if (result)
+ return result;
+
+ // set up an additional bank for the upper part of the low bank when split
+ memory_region *const romregion(cart_rom_region());
+ m_bank_mask_rom_upper = device_generic_cart_interface::map_non_power_of_two(
+ unsigned(romregion->bytes() / 0x4000),
+ [this, base = &romregion->as_u8()] (unsigned entry, unsigned page)
+ {
+ LOG(
+ "Install ROM 0x%06X-0x%06X in upper bank entry %u\n",
+ page * 0x4000,
+ (page * 0x4000) + 0x3fff,
+ entry);
+ m_bank_rom_upper->configure_entry(entry, &base[page * 0x4000]);
+ });
+ m_bank_rom_upper->set_entry(0);
+
+ // install handlers with protection emulation
+ cart_space()->install_read_handler(
+ 0x0000, 0x7fff,
+ emu::rw_delegate(*this, FUNC(vf001_device::read_rom)));
+ cart_space()->install_write_handler(
+ 0x6000, 0x700f, 0x100f, 0x0000, 0x0000,
+ emu::rw_delegate(*this, FUNC(vf001_device::prot_cmd)));
+
+ // all good
+ return std::error_condition();
}
- else if (offset < 0x6000)
+
+protected:
+ virtual void device_start() override ATTR_COLD
{
- m_ram_bank = data & 0x1f;
+ mbc5_logo_spoof_device_base::device_start();
+
+ m_prot_cmd_data = 0U;
+ m_readback_address = 0U;
+ m_readback_bank = 0U;
+ std::fill(std::begin(m_readback_data), std::end(m_readback_data), 0U);
+ m_split_address = 0U;
+
+ save_item(NAME(m_prot_cmd_enable));
+ save_item(NAME(m_prot_cmd_data));
+ save_item(NAME(m_readback_address));
+ save_item(NAME(m_readback_bank));
+ save_item(NAME(m_readback_length));
+ save_item(NAME(m_readback_data));
+ save_item(NAME(m_readback_count));
+ save_item(NAME(m_split_address));
+ save_item(NAME(m_split_enable));
}
-}
-uint8_t gb_rom_camera_device::read_ram(offs_t offset)
-{
- if ((m_ram_bank & 0x10) != 0)
- return (offset == 0) ? (m_camera_regs[0] & 0x7) : 0;
- if (!m_ram.empty() && (m_camera_regs[0] & 0x1) == 0)
+ virtual void device_reset() override ATTR_COLD
{
- /* Use first saved image as the snapshot. Top and bottom of snapshot are not saved. */
- if (m_ram_bank == 0 && offset >= 0x100 && offset < 0xf00)
- return m_ram[0x1f00 + offset];
- return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)];
+ mbc5_logo_spoof_device_base::device_reset();
+
+ m_prot_cmd_enable = 0U;
+ m_readback_length = 0U;
+ m_readback_count = 0U;
+ m_split_enable = 0U;
}
- return 0;
-}
-void gb_rom_camera_device::write_ram(offs_t offset, uint8_t data)
-{
- if ((m_ram_bank & 0x10) != 0)
+private:
+ u8 read_rom(offs_t offset)
{
- if (offset == 0)
+ u8 const data(mbc5_logo_spoof_device_base::read_rom(offset));
+
+ if (!m_readback_count && m_readback_length && !machine().side_effects_disabled())
{
- m_camera_regs[0] = data & 0x7;
- if (data & 0x1) update_camera();
+ if ((offset == m_readback_address) && ((BIT(offset, 14) ? bank_rom_fine() : 0) == m_readback_bank))
+ {
+ LOG(
+ "%s: Readback triggered by read from ROM bank 0x%02X address 0x%04X, length = %u\n",
+ machine().describe_context(),
+ m_readback_bank,
+ m_readback_address,
+ m_readback_length);
+ m_readback_count = m_readback_length;
+ }
}
- else if (offset < 54)
+
+ if (m_readback_count)
{
- m_camera_regs[offset] = data;
+ u8 const index(m_readback_length - m_readback_count);
+ if (!machine().side_effects_disabled())
+ --m_readback_count;
+ LOG(
+ "%s: Readback %u = 0x%02X\n",
+ machine().describe_context(),
+ index,
+ m_readback_data[index]);
+ return m_readback_data[index];
}
+
+ bool const split_upper(m_split_enable && (m_split_address <= offset) && (0x4000 > offset));
+ return !split_upper ? data : reinterpret_cast<u8 const *>(m_bank_rom_upper->base())[offset];
}
- else if (m_ram_enable && (m_camera_regs[0] & 0x1) == 0)
+
+ void prot_cmd(offs_t offset, u8 data)
{
- // RAM
- if (!m_ram.empty())
- m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data;
+ if ((0x1000 == offset) && (0x96 == data))
+ {
+ m_prot_cmd_enable = 1U;
+ m_prot_cmd_data = m_command_preload;
+ LOG(
+ "%s: Enabled protection commands, initial data = 0x%02X\n",
+ machine().describe_context(),
+ m_prot_cmd_data);
+ }
+ else if ((0x100f == offset) && (0x96 == data))
+ {
+ LOG("%s: Disabled protection commands\n", machine().describe_context());
+ m_prot_cmd_enable = 0U;
+ }
+ else if (m_prot_cmd_enable)
+ {
+ if (((0x0001 <= offset) && (0x1000 > offset)) || (0x100b <= offset))
+ {
+ logerror(
+ "%s: Unknown protection command address 0x%04X = 0x%02X\n",
+ machine().describe_context(),
+ offset,
+ data);
+ return;
+ }
+
+ // previous data is rotated right and combined with new data
+ m_prot_cmd_data = ((m_prot_cmd_data >> 1) | (m_prot_cmd_data << 7)) ^ data;
+
+ switch (offset)
+ {
+ case 0x0000:
+ m_bank_rom_upper->set_entry(m_prot_cmd_data & m_bank_mask_rom_upper);
+ LOG(
+ "%s: Bank split upper bank = 0x%02X\n",
+ machine().describe_context(),
+ m_prot_cmd_data);
+ break;
+ case 0x1000:
+ if (BIT(m_prot_cmd_data, 2))
+ {
+ m_readback_length = (m_prot_cmd_data & 0x03) + 1;
+ LOG(
+ "%s: Readback length = %u\n",
+ machine().describe_context(),
+ m_readback_length);
+ }
+ break;
+ case 0x1001:
+ case 0x1002:
+ m_readback_address &= BIT(offset, 0) ? 0xff00 : 0x00ff;
+ m_readback_address |= u16(m_prot_cmd_data) << (BIT(offset, 0) ? 0 : 8);
+ LOG(
+ "%s: Readback address = 0x%04X\n",
+ machine().describe_context(),
+ m_readback_address);
+ break;
+ case 0x1003:
+ m_readback_bank = m_prot_cmd_data;
+ LOG(
+ "%s: Readback bank = 0x%02X\n",
+ machine().describe_context(),
+ m_readback_bank);
+ break;
+ case 0x1004:
+ case 0x1005:
+ case 0x1006:
+ case 0x1007:
+ m_readback_data[offset & 0x0003] = m_prot_cmd_data;
+ LOG(
+ "%s: Readback data %u = 0x%02X\n",
+ machine().describe_context(),
+ offset & 0x0003,
+ m_prot_cmd_data);
+ break;
+ case 0x1008:
+ m_split_enable = (0x0f == (m_prot_cmd_data & 0x0f)) ? 1U : 0U;
+ LOG(
+ "%s: Bank split %s\n",
+ machine().describe_context(),
+ m_split_enable ? "enabled" : "disabled");
+ break;
+ case 0x1009:
+ case 0x100a:
+ m_split_address &= BIT(offset, 0) ? 0xff00 : 0x00ff;
+ m_split_address |= u16(m_prot_cmd_data) << (BIT(offset, 0) ? 0 : 8);
+ LOG(
+ "%s: Bank split address = 0x%04X\n",
+ machine().describe_context(),
+ m_split_address);
+ break;
+ }
+ }
}
-}
+
+ memory_bank_creator m_bank_rom_upper;
+ u8 m_bank_mask_rom_upper;
+ u8 m_command_preload;
+
+ u8 m_prot_cmd_enable;
+ u8 m_prot_cmd_data;
+
+ u16 m_readback_address;
+ u8 m_readback_bank;
+ u8 m_readback_length;
+ u8 m_readback_data[4];
+ u8 m_readback_count;
+
+ u16 m_split_address;
+ u8 m_split_enable;
+};
+
+} // anonymous namespace
+
+} // namespace bus::gameboy
+
+
+// device type definition
+DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_MBC1, device_gb_cart_interface, bus::gameboy::mbc1_device, "gb_rom_mbc1", "Game Boy MBC1 Cartridge")
+DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_MBC5, device_gb_cart_interface, bus::gameboy::mbc5_device, "gb_rom_mbc5", "Game Boy MBC5 Cartridge")
+DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_BBD, device_gb_cart_interface, bus::gameboy::bbd_device, "gb_rom_bbd", "Game Boy BBD Cartridge")
+DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_DSHGGB81, device_gb_cart_interface, bus::gameboy::dshggb81_device, "gb_rom_dshggb81", "Game Boy DSHGGB-81 Cartridge")
+DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_SINTAX, device_gb_cart_interface, bus::gameboy::sintax_device, "gb_rom_sintax", "Game Boy Sintax MBC5 Cartridge")
+DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_CHONGWU, device_gb_cart_interface, bus::gameboy::chongwu_device, "gb_rom_chongwu", "Game Boy Chongwu Xiao Jingling Pokemon Pikecho Cartridge")
+DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_LICHENG, device_gb_cart_interface, bus::gameboy::licheng_device, "gb_rom_licheng", "Game Boy Li Cheng MBC5 Cartridge")
+DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_NEWGBCHK, device_gb_cart_interface, bus::gameboy::ngbchk_device, "gb_rom_ngbchk", "Game Boy HK0701/HK0819 Cartridge")
+DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_VF001, device_gb_cart_interface, bus::gameboy::vf001_device, "gb_rom_vf001", "Game Boy Vast Fame VF001 Cartridge")