summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2022-10-04 12:48:26 +1100
committer Vas Crabb <vas@vastheman.com>2022-10-04 12:51:33 +1100
commitf4b23f173771db595251ea3e1117445f28a8baf0 (patch)
tree54e9fc734a7b30736519ffe075ba15311a757999
parenta272716bce69c16816088fb1f1fb808d7b94c66f (diff)
bus/gameboy: Moved MBC3/MBC30 to their own file as separate devices.
New working software list additions ----------------------------------- gbcolor.xml: Juéduì Wǔlì (China), Wàixīng Tànxiǎn zhī Xīngqiú Dàzhàn (China) [Robyn A1200, taizou]
-rw-r--r--hash/apple2gs_flop_orig.xml2
-rw-r--r--hash/gbcolor.xml39
-rw-r--r--scripts/src/bus.lua2
-rw-r--r--src/devices/bus/gameboy/carts.cpp3
-rw-r--r--src/devices/bus/gameboy/mbc.cpp543
-rw-r--r--src/devices/bus/gameboy/mbc.h1
-rw-r--r--src/devices/bus/gameboy/mbc3.cpp721
-rw-r--r--src/devices/bus/gameboy/mbc3.h19
-rw-r--r--src/devices/cpu/mn1880/mn1880.cpp2
-rw-r--r--src/mame/atari/atarigt.cpp2
-rw-r--r--src/mame/jaleco/armchamp.cpp6
11 files changed, 783 insertions, 557 deletions
diff --git a/hash/apple2gs_flop_orig.xml b/hash/apple2gs_flop_orig.xml
index 3dda1807eae..1a5a1ab8a5f 100644
--- a/hash/apple2gs_flop_orig.xml
+++ b/hash/apple2gs_flop_orig.xml
@@ -4041,7 +4041,7 @@ license:CC0
<software name="frltwrd">
<description>First Letters and Words (Version 1.0)</description>
<year>????</year>
- <publisher>First Byte</publisher>
+ <publisher>First Byte</publisher>
<info name="release" value="2022-09-10"/>
<sharedfeat name="compatibility" value="A2GS"/>
<!-- It requires a 768K Apple IIgs. -->
diff --git a/hash/gbcolor.xml b/hash/gbcolor.xml
index 4d5402d0448..f02c541ffaa 100644
--- a/hash/gbcolor.xml
+++ b/hash/gbcolor.xml
@@ -26816,10 +26816,10 @@ license:CC0
<info name="alt_title" value="真三國無雙2"/>
<part name="cart" interface="gameboy_cart">
<feature name="slot" value="rom_sintax" />
- <dataarea name="rom" size="2097152">
- <rom name="zhen san guo wu shuang 2 - shin sangokumusou 2 (chi)(unl) [raw dump].bin" size="2097152" crc="95c322c9" sha1="e6ab58d212de83cb18e9eadac4cee5d0f6e4e31f"/>
+ <dataarea name="rom" size="0x200000">
+ <rom name="zhen san guo wu shuang 2 - shin sangokumusou 2 (chi)(unl) [raw dump].bin" size="0x200000" crc="95c322c9" sha1="e6ab58d212de83cb18e9eadac4cee5d0f6e4e31f"/>
</dataarea>
- <dataarea name="nvram" size="8192">
+ <dataarea name="nvram" size="0x2000">
</dataarea>
</part>
</software>
@@ -26831,10 +26831,37 @@ license:CC0
<info name="alt_title" value="真三國無雙2"/>
<part name="cart" interface="gameboy_cart">
<feature name="slot" value="rom_mbc5" />
- <dataarea name="rom" size="2097152">
- <rom name="shin san guo shi 2 (unl).bin" size="2097152" crc="33f56c90" sha1="a5111c11cf70c65341b373f484650caae3c4d29f"/>
+ <dataarea name="rom" size="0x200000">
+ <rom name="shin san guo shi 2 (unl).bin" size="0x200000" crc="33f56c90" sha1="a5111c11cf70c65341b373f484650caae3c4d29f"/>
</dataarea>
- <dataarea name="nvram" size="8192">
+ <dataarea name="nvram" size="0x2000">
+ </dataarea>
+ </part>
+ </software>
+
+ <software name="jueduiwl">
+ <description>Juéduì Wǔlì (China)</description>
+ <year>200?</year>
+ <publisher>Sintax</publisher>
+ <info name="alt_title" value="絕對武力" />
+ <part name="cart" interface="gameboy_cart">
+ <feature name="slot" value="rom_sintax" />
+ <dataarea name="rom" size="0x200000">
+ <rom name="juedui wuli.gbc" size="0x200000" crc="f4d63a7e" sha1="f0b9f02335ee89aa6189e687e6e759d8c49bc2b3"/>
+ </dataarea>
+ </part>
+ </software>
+
+ <software name="wxtxxqdz">
+ <!-- not sure how the hiragana の (no) in the title should be read - assuming it's a stylised 之 -->
+ <description>Wàixīng Tànxiǎn zhī Xīngqiú Dàzhàn (China)</description>
+ <year>200?</year>
+ <publisher>Sintax</publisher>
+ <info name="alt_title" value="外星探險の星球大戰" />
+ <part name="cart" interface="gameboy_cart">
+ <feature name="slot" value="rom_sintax" />
+ <dataarea name="rom" size="0x200000">
+ <rom name="waixing tanxian xingqiu dazhan.gbc" size="0x200000" crc="4e600093" sha1="32067be39f0bdc354c31a7a02f24ba00b61ae4f1"/>
</dataarea>
</part>
</software>
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua
index 7c646731f36..4fabd53ea76 100644
--- a/scripts/src/bus.lua
+++ b/scripts/src/bus.lua
@@ -3709,6 +3709,8 @@ if (BUSES["GAMEBOY"]~=null) then
MAME_DIR .. "src/devices/bus/gameboy/mbc.h",
MAME_DIR .. "src/devices/bus/gameboy/mbc2.cpp",
MAME_DIR .. "src/devices/bus/gameboy/mbc2.h",
+ MAME_DIR .. "src/devices/bus/gameboy/mbc3.cpp",
+ MAME_DIR .. "src/devices/bus/gameboy/mbc3.h",
MAME_DIR .. "src/devices/bus/gameboy/mbc6.cpp",
MAME_DIR .. "src/devices/bus/gameboy/mbc6.h",
MAME_DIR .. "src/devices/bus/gameboy/mbc7.cpp",
diff --git a/src/devices/bus/gameboy/carts.cpp b/src/devices/bus/gameboy/carts.cpp
index 8bb63cf3cbd..dbd4924d71b 100644
--- a/src/devices/bus/gameboy/carts.cpp
+++ b/src/devices/bus/gameboy/carts.cpp
@@ -15,6 +15,7 @@
#include "huc3.h"
#include "mbc.h"
#include "mbc2.h"
+#include "mbc3.h"
#include "mbc6.h"
#include "mbc7.h"
#include "mmm01.h"
@@ -80,7 +81,7 @@ void gameboy_cartridges(device_slot_interface &device)
device.option_add_internal(slotoptions::GB_MBC1, GB_ROM_MBC1);
device.option_add_internal(slotoptions::GB_MBC2, GB_ROM_MBC2);
device.option_add_internal(slotoptions::GB_MBC3, GB_ROM_MBC3);
- device.option_add_internal(slotoptions::GB_MBC30, GB_ROM_MBC3); // MBC3 and MBC30 treated as the same thing for now
+ device.option_add_internal(slotoptions::GB_MBC30, GB_ROM_MBC30);
device.option_add_internal(slotoptions::GB_MBC5, GB_ROM_MBC5);
device.option_add_internal(slotoptions::GB_MBC6, GB_ROM_MBC6);
device.option_add_internal(slotoptions::GB_MBC7_2K, GB_ROM_MBC7_2K);
diff --git a/src/devices/bus/gameboy/mbc.cpp b/src/devices/bus/gameboy/mbc.cpp
index a31b8d8f858..4bad4a2522d 100644
--- a/src/devices/bus/gameboy/mbc.cpp
+++ b/src/devices/bus/gameboy/mbc.cpp
@@ -31,34 +31,6 @@
Regular ROM aliasing rules apply.
- MBC3 Mapper
- ===========
-
- The MBC3 mapper cartridges can include a RTC chip.
-
- 0000-1FFF - Writing to this area enables (value 0x0A) or disables (not 0x0A) the
- SRAM and RTC registers.
- 2000-3FFF - Writing to this area selects the ROM bank to appear at 4000-7FFF.
- Bits 6-0 are used to select the bank number. If a value of
- 0bX0000000 is written then this is automatically changed into
- 0bX0000001 by the mapper.
- 4000-5FFF - Writing to this area selects the RAM bank or the RTC register to
- read.
- XXXX00bb - Select RAM bank bb.
- XXXX1rrr - Select RTC register rrr. Accepted values for rrr are:
- 000 - Seconds (0x00-0x3B)
- 001 - Minutes (0x00-0x3B)
- 010 - Hours (0x00-0x17)
- 011 - Bits 7-0 of the day counter
- 100 - bit 0 - Bit 8 of the day counter
- bit 6 - Halt RTC timer ( 0 = timer active, 1 = halted)
- bit 7 - Day counter overflow flag
- 6000-7FFF - Writing 0x00 followed by 0x01 latches the RTC data. This latching
- method is used for reading the RTC registers.
-
- Regular ROM aliasing rules apply.
-
-
MBC5 Mapper
===========
@@ -73,11 +45,6 @@
TODO:
- * What does MBC3 do with the RAM bank outputs when RTC is selected?
- * How do MBC3 invalid second/minute/hour values roll over?
- * For convenience, MBC3 and MBC30 are emulated as one device for now.
- - MBC3 only has 2 RAM bank outputs, but it will allow 3 like MBC30 here.
- - MBC30 supposedly has 8 ROM bank outputs, but the one game using it only needs 7.
* 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
@@ -96,8 +63,6 @@
#include "bus/generic/slot.h"
-#include "dirtc.h"
-
#include "corestr.h"
#include <algorithm>
@@ -188,12 +153,7 @@ protected:
set_bank_ram(data & m_bank_lines[1]);
}
- auto &view_aux(unsigned entry) { return m_view_ram[entry + 1]; }
- void set_view_aux(unsigned entry) { m_view_ram.select(entry + 1); }
-
private:
- static inline constexpr unsigned PAGE_RAM_SIZE = 0x2000;
-
memory_view m_view_ram;
u16 m_bank_lines[2];
@@ -567,508 +527,6 @@ private:
//**************************************************************************
-// MBC3 (max 8 MiB ROM, max 32 KiB SRAM, RTC)
-//**************************************************************************
-
-class mbc3_device : public rom_mbc_device_base, public device_rtc_interface, public device_nvram_interface
-{
-public:
- mbc3_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
- rom_mbc_device_base(mconfig, GB_ROM_MBC3, tag, owner, clock),
- device_rtc_interface(mconfig, *this),
- device_nvram_interface(mconfig, *this),
- m_timer_rtc(nullptr),
- m_machine_seconds(0),
- m_has_rtc_xtal(false),
- m_has_battery(false),
- m_rtc_enable(0U),
- m_rtc_select(0U),
- m_rtc_latch(0U)
- {
- }
-
- virtual image_init_result load(std::string &message) override ATTR_COLD
- {
- // check for RTC oscillator and backup battery
- if (loaded_through_softlist())
- {
- // there's a feature tag indicating presence or absence of RTC crystal
- char const *const rtcfeature(get_feature("rtc"));
- if (rtcfeature)
- {
- // explicitly specified in software list
- if (util::streqlower(rtcfeature, "yes") || util::streqlower(rtcfeature, "true"))
- {
- logerror("Real-time clock crystal present\n");
- m_has_rtc_xtal = true;
- }
- else if (util::streqlower(rtcfeature, "no") || util::streqlower(rtcfeature, "false"))
- {
- logerror("No real-time clock crystal present\n");
- m_has_rtc_xtal = false;
- }
- else
- {
- message = "Invalid 'rtc' feature value (must be yes or no)";
- return image_init_result::FAIL;
- }
- }
- else
- {
- logerror("No 'rtc' feature found, assuming no real-time clock crystal present\n");
- m_has_rtc_xtal = false;
- }
-
- // if there's an NVRAM region, there must be a backup battery
- if (cart_nvram_region())
- {
- logerror("Found 'nvram' region, backup battery must be present\n");
- m_has_battery = true;
- }
- else
- {
- logerror("No 'nvram' region found, assuming no backup battery present\n");
- m_has_battery = true;
- }
- }
- else
- {
- gbxfile::leader_1_0 leader;
- u8 const *extra;
- u32 extralen;
- if (gbxfile::get_data(gbx_footer_region(), leader, extra, extralen))
- {
- m_has_rtc_xtal = bool(leader.rtc);
- m_has_battery = bool(leader.batt);
- logerror(
- "GBX format image specifies %sreal-time clock crystal present, %sbackup battery present\n",
- m_has_rtc_xtal ? "" : "no ",
- m_has_battery ? "" : "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_MBC3_RTC_BATT:
- case cartheader::TYPE_MBC3_RTC_RAM_BATT:
- m_has_rtc_xtal = true;
- m_has_battery = true;
- break;
- case cartheader::TYPE_MBC3:
- m_has_rtc_xtal = false;
- m_has_battery = false;
- break;
- case cartheader::TYPE_MBC3_RAM:
- case cartheader::TYPE_MBC3_RAM_BATT:
- m_has_rtc_xtal = false;
- m_has_battery = true;
- break;
- default:
- osd_printf_warning(
- "[%s] Unrecognized cartridge type 0x%02X in header, assuming no real-time clock crystal or backup battery present\n",
- tag(),
- carttype);
- m_has_rtc_xtal = false;
- m_has_battery = false;
- }
- logerror(
- "Cartridge type 0x%02X in header, %sreal-time clock crystal present, %sbackup battery present\n",
- carttype,
- m_has_rtc_xtal ? "" : "no ",
- m_has_battery ? "" : "no ");
- }
- }
- }
-
- // set up ROM and RAM
- if (!install_memory(message, 3, 7))
- return image_init_result::FAIL;
-
- // install bank switching handlers
- cart_space()->install_write_handler(
- 0x0000, 0x1fff,
- write8smo_delegate(*this, FUNC(mbc3_device::enable_ram_rtc)));
- cart_space()->install_write_handler(
- 0x2000, 0x3fff,
- write8smo_delegate(*this, FUNC(mbc3_device::bank_switch_fine)));
- cart_space()->install_write_handler(
- 0x4000, 0x5fff,
- write8smo_delegate(*this, FUNC(mbc3_device::select_ram_rtc)));
- cart_space()->install_write_handler(
- 0x6000, 0x7fff,
- write8smo_delegate(*this, FUNC(mbc3_device::latch_rtc)));
-
- // install real-time clock handlers
- view_aux(0).install_read_handler(
- 0xa000, 0xbfff,
- read8mo_delegate(*this, FUNC(mbc3_device::read_rtc)));
- view_aux(0).install_write_handler(
- 0xa000, 0xbfff,
- write8smo_delegate(*this, FUNC(mbc3_device::write_rtc)));
-
- // if real-time clock crystal is present, start it ticking
- if (m_has_rtc_xtal)
- {
- logerror("Real-time clock crystal present, starting timer\n");
- m_timer_rtc->adjust(attotime(1, 0), 0, attotime(1, 0));
- }
-
- // all good
- return image_init_result::PASS;
- };
-
-protected:
- virtual void device_start() override ATTR_COLD
- {
- rom_mbc_device_base::device_start();
-
- m_timer_rtc = timer_alloc(FUNC(mbc3_device::rtc_advance_seconds), this);
-
- save_item(NAME(m_rtc_regs));
- save_item(NAME(m_rtc_enable));
- save_item(NAME(m_rtc_select));
- save_item(NAME(m_rtc_latch));
- }
-
- virtual void device_reset() override ATTR_COLD
- {
- rom_mbc_device_base::device_reset();
-
- m_rtc_enable = 0U;
- m_rtc_select = 0U;
- m_rtc_latch = 0U;
-
- set_bank_rom_coarse(0);
- set_bank_rom_fine(1);
- set_ram_enable(false);
- set_bank_ram(0);
- }
-
- virtual void rtc_clock_updated(
- int year,
- int month,
- int day,
- int day_of_week,
- int hour,
- int minute,
- int second) override ATTR_COLD
- {
- if (!m_has_rtc_xtal && !m_has_battery)
- {
- logerror("No real-time clock crystal or no battery present, not updating for elapsed time\n");
- }
- else if (std::numeric_limits<s64>::min() == m_machine_seconds)
- {
- logerror("Failed to load machine time from previous session, not updating for elapsed time\n");
- }
- else if (BIT(m_rtc_regs[0][4], 6))
- {
- logerror("Real-time clock halted, not updating for elapsed time\n");
- }
- else
- {
- // do a simple seconds elapsed since last run calculation
- system_time current;
- machine().current_datetime(current);
- s64 delta(std::make_signed_t<decltype(current.time)>(current.time) - m_machine_seconds);
- logerror("Previous session time, %d current time %d, delta %d\n", current.time, m_machine_seconds, delta);
- if (0 > delta)
- {
- // This happens if the user runs the emulation faster
- // than real time, exits, and then starts again without
- // waiting for the difference between emulated and real
- // time to elapse.
- logerror("Previous session ended in the future, not updating for elapsed time\n");
- }
- else
- {
- logerror(
- "Time before applying delta %u %02u:%02u:%02u%s\n",
- (u16(BIT(m_rtc_regs[0][4], 0)) << 8) | m_rtc_regs[0][3],
- m_rtc_regs[0][2],
- m_rtc_regs[0][1],
- m_rtc_regs[0][0],
- BIT(m_rtc_regs[0][4], 7) ? " (overflow)" : "");
-
- // annoyingly, we can get two rollovers if we started with an invalid value
- unsigned seconds(delta % 60);
- delta /= 60;
- if (60 <= m_rtc_regs[0][0])
- {
- m_rtc_regs[0][0] = 0U;
- --seconds;
- ++delta;
- }
- if (60 <= (m_rtc_regs[0][0] + seconds))
- ++delta;
- m_rtc_regs[0][0] = (m_rtc_regs[0][0] + seconds) % 60;
-
- // minutes is the same
- unsigned minutes(delta % 60);
- delta /= 60;
- if (60 <= m_rtc_regs[0][1])
- {
- m_rtc_regs[0][1] = 0U;
- --minutes;
- ++delta;
- }
- if (60 <= (m_rtc_regs[0][1] + minutes))
- ++delta;
- m_rtc_regs[0][1] = (m_rtc_regs[0][1] + minutes) % 60;
-
- // hours just has a different rollover point
- unsigned hours(delta % 24);
- delta /= 24;
- if (24 <= m_rtc_regs[0][2])
- {
- m_rtc_regs[0][2] = 0U;
- --hours;
- ++delta;
- }
- if (24 <= (m_rtc_regs[0][2] + hours))
- ++delta;
- m_rtc_regs[0][2] = (m_rtc_regs[0][2] + hours) % 24;
-
- // days has simple binary rollover
- unsigned days(delta % 256);
- if (256 <= (m_rtc_regs[0][3] + days))
- ++delta;
- m_rtc_regs[0][3] += days;
-
- // set overflow flag if appropriate
- if ((1 < delta) || (BIT(m_rtc_regs[0][4], 0) && delta))
- m_rtc_regs[0][4] |= 0x80;
- m_rtc_regs[0][4] ^= BIT(delta, 0);
-
- logerror(
- "Time after applying delta %u %02u:%02u:%02u%s\n",
- (u16(BIT(m_rtc_regs[0][4], 0)) << 8) | m_rtc_regs[0][3],
- m_rtc_regs[0][2],
- m_rtc_regs[0][1],
- m_rtc_regs[0][0],
- BIT(m_rtc_regs[0][4], 7) ? " (overflow)" : "");
- }
- }
- }
-
- virtual void nvram_default() override ATTR_COLD
- {
- // TODO: proper cold RTC state
- m_machine_seconds = std::numeric_limits<s64>::min();
- for (unsigned i = 0U; std::size(m_rtc_regs[0]) > i; ++i)
- m_rtc_regs[0][i] = RTC_MASK[i];
- }
-
- virtual bool nvram_read(util::read_stream &file) override ATTR_COLD
- {
- if (m_has_battery)
- {
- // read previous machine time (seconds since epoch) and RTC registers
- u64 seconds;
- std::size_t actual;
- if (file.read(&seconds, sizeof(seconds), actual) || (sizeof(seconds) != actual))
- return false;
- m_machine_seconds = big_endianize_int64(seconds);
-
- if (file.read(&m_rtc_regs[0][0], sizeof(m_rtc_regs[0]), actual) || (sizeof(m_rtc_regs[0]) != actual))
- return false;
- }
- else
- {
- logerror("No battery present, not loading real-time clock register contents\n");
- }
- return true;
- }
-
- virtual bool nvram_write(util::write_stream &file) override ATTR_COLD
- {
- // save current machine time as seconds since epoch and RTC registers
- system_time current;
- machine().current_datetime(current);
- u64 const seconds(big_endianize_int64(s64(std::make_signed_t<decltype(current.time)>(current.time))));
- std::size_t written;
- if (file.write(&seconds, sizeof(seconds), written) || (sizeof(seconds) != written))
- return false;
- if (file.write(&m_rtc_regs[0][0], sizeof(m_rtc_regs[0]), written) || (sizeof(m_rtc_regs[0]) != written))
- return false;
- return true;
- }
-
- virtual bool nvram_can_write() const override ATTR_COLD
- {
- return m_has_battery;
- }
-
-private:
- static inline constexpr u8 RTC_MASK[]{ 0x3f, 0x3f, 0x1f, 0xff, 0xc1 };
- static inline constexpr u8 RTC_ROLLOVER[]{ 0x3c, 0x3c, 0x18, 0x00, 0x00 };
-
- TIMER_CALLBACK_MEMBER(rtc_advance_seconds)
- {
- if (BIT(m_rtc_regs[0][4], 6))
- return;
-
- if (rtc_increment(0))
- return;
- if (rtc_increment(1))
- return;
- if (rtc_increment(2))
- return;
- if (++m_rtc_regs[0][3])
- return;
-
- if (BIT(m_rtc_regs[0][4], 0))
- {
- LOG("Day counter overflow");
- m_rtc_regs[0][4] |= 0x80;
- }
- m_rtc_regs[0][4] ^= 0x01;
- }
-
- void enable_ram_rtc(u8 data)
- {
- m_rtc_enable = (0x0a == (data & 0x0f)) ? 1U : 0U;
- if (!m_rtc_enable)
- {
- set_ram_enable(false);
- }
- else if (BIT(m_rtc_select, 3))
- {
- LOG(
- "%s: RTC register %u enabled\n",
- machine().describe_context(),
- m_rtc_select & 0x07);
- set_view_aux(0);
- }
- else
- {
- set_ram_enable(true);
- }
- }
-
- void bank_switch_fine(u8 data)
- {
- data &= 0x7f;
- set_bank_rom_fine(data ? data : 1);
- }
-
- void select_ram_rtc(u8 data)
- {
- // TODO: what happens with the RAM bank outputs when the RTC is selected?
- // TODO: what happens for 4-7?
- // TODO: is the high nybble ignored altogether?
- bank_switch_coarse(data & 0x07);
- m_rtc_select = data;
- if (m_rtc_enable)
- {
- if (BIT(data, 3))
- {
- LOG(
- "%s: RTC register %u enabled\n",
- machine().describe_context(),
- data & 0x07);
- set_view_aux(0);
- }
- else
- {
- set_ram_enable(true);
- }
- }
- }
-
- void latch_rtc(u8 data)
- {
- // FIXME: does it just check the least significant bit, or does it look for 0x00 and 0x01?
- LOG("Latch RTC 0x%02X -> 0x%02X\n", m_rtc_latch, data);
- if (!BIT(m_rtc_latch, 0) && BIT(data, 0))
- {
- LOG("%s: Latching RTC registers\n", machine().describe_context());
- std::copy(std::begin(m_rtc_regs[0]), std::end(m_rtc_regs[0]), std::begin(m_rtc_regs[1]));
- }
- m_rtc_latch = data;
- }
-
- u8 read_rtc(address_space &space)
- {
- u8 const reg(m_rtc_select & 0x07);
- if (std::size(m_rtc_regs[1]) > reg)
- {
- LOG(
- "%s: Read RTC register %u = 0x%02X\n",
- machine().describe_context(),
- reg,
- m_rtc_regs[1][reg]);
- return m_rtc_regs[1][reg];
- }
- else
- {
- LOG(
- "%s: Read invalid RTC register %u\n",
- machine().describe_context(),
- reg);
- return space.unmap();
- }
- }
-
- void write_rtc(u8 data)
- {
- u8 const reg(m_rtc_select & 0x07);
- if (std::size(m_rtc_regs[0]) > reg)
- {
- LOG(
- "%s: Write RTC register %u = 0x%02X\n",
- machine().describe_context(),
- reg,
- data);
- if (4U == reg)
- {
- // TODO: are bits 5-1 physically present, and if not, what do they read as?
- // TODO: how does halting the RTC interact with the prescaler?
- data &= 0xc1;
- m_rtc_regs[0][reg] = data;
- }
- else
- {
- m_rtc_regs[0][reg] = data;
- }
- }
- else
- {
- LOG(
- "%s: Write invalid RTC register %u = 0x%02X\n",
- machine().describe_context(),
- reg,
- data);
- }
- }
-
- u8 rtc_increment(unsigned index)
- {
- m_rtc_regs[0][index] = (m_rtc_regs[0][index] + 1) & RTC_MASK[index];
- if (RTC_ROLLOVER[index] == (m_rtc_regs[0][index] & RTC_ROLLOVER[index]))
- m_rtc_regs[0][index] = 0U;
- return m_rtc_regs[0][index];
- }
-
- emu_timer *m_timer_rtc;
- s64 m_machine_seconds;
- bool m_has_rtc_xtal;
- bool m_has_battery;
-
- u8 m_rtc_regs[2][5];
- u8 m_rtc_enable;
- u8 m_rtc_select;
- u8 m_rtc_latch;
-};
-
-
-
-//**************************************************************************
// MBC5 (max 128 MiB ROM, max 128 KiB SRAM)
//**************************************************************************
@@ -1896,7 +1354,6 @@ private:
// 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_MBC3, device_gb_cart_interface, bus::gameboy::mbc3_device, "gb_rom_mbc3", "Game Boy MBC3/MBC30 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_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")
diff --git a/src/devices/bus/gameboy/mbc.h b/src/devices/bus/gameboy/mbc.h
index f7e7301a5c3..6d492a779a2 100644
--- a/src/devices/bus/gameboy/mbc.h
+++ b/src/devices/bus/gameboy/mbc.h
@@ -9,7 +9,6 @@
DECLARE_DEVICE_TYPE(GB_ROM_MBC1, device_gb_cart_interface)
-DECLARE_DEVICE_TYPE(GB_ROM_MBC3, device_gb_cart_interface)
DECLARE_DEVICE_TYPE(GB_ROM_MBC5, device_gb_cart_interface)
DECLARE_DEVICE_TYPE(GB_ROM_MMM01, device_gb_cart_interface)
DECLARE_DEVICE_TYPE(GB_ROM_SINTAX, device_gb_cart_interface)
diff --git a/src/devices/bus/gameboy/mbc3.cpp b/src/devices/bus/gameboy/mbc3.cpp
new file mode 100644
index 00000000000..bfead355999
--- /dev/null
+++ b/src/devices/bus/gameboy/mbc3.cpp
@@ -0,0 +1,721 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ Nintendo Game Boy Memory Bank Controller 3
+
+ Includes a real-time clock in addition to the memory controller features.
+ The real-time clock can be used with or without a backup battery. Without
+ a backup battery, it just keeps time while the game is running. Several
+ games also used this memory controller without a real-time clock crystal.
+
+ The MBC3 supports up to 2 MiB ROM (128 16 KiB pages) and 32 KiB static RAM
+ (4 8 KiB pages). The MBC30 supports up to 4 MiB ROM (256 16 KiB pages) and
+ 64 KiB static RAM (8 8 KiB pages). If RAM bank lines are used for coarse
+ ROM banking, up to 8 MiB ROM (512 16 KiB pages) can be supported by the
+ MBC3, and up to 32 MiB ROM (2048 16 KiB pages) can be supported by the
+ MBC30.
+
+ The MBC30 was only used for a single game. In practice, 128K*8 static RAM
+ chips were used, but only half the space is accessible as the MBC30 chip
+ only has three RAM bank output lines.
+
+ 0x0000-3FFF R - Fixed ROM bank, always first page of ROM.
+ 0x4000-7FFF R - Selectable ROM bank, page 0-127 (MBC) or 0-255 (MBC30) of
+ ROM.
+ 0xA000-BFFF RW - Static RAM or real-time clock registers.
+
+ 0x0000-1FFF W - I/O enable - write 0x0A on low nybble to enable static RAM
+ or real-time clock registers, any other value to disable.
+ 0x2000-3FFF W - Select ROM page mapped at 0x4000. Bit 7 is ignored by
+ MBC3; all bits are significant for MBC30 Writing 0 selects
+ page 1.
+ 0x4000-5FFF W - Select RAM page or real-time clock register mapped at
+ 0xA000 (if enabled). If bit 3 is clear, static RAM is
+ selected; if bit 3 is set, real-time clock registers are
+ selected. Bits 2-0 select the RAM page or register.
+ 0x6000-7FFF W - Write 0x00 followed by 0x01 to latch real-time clock
+ registers.
+
+ Real-time clock registers:
+ 0x0 - Seconds.
+ 0x1 - Minutes.
+ 0x2 - Hours.
+ 0x3 - Bits 7-0 of days.
+ 0x4 - X------- Day counter overflow (set on carry out of bit 8, cleared
+ manually).
+ -X------ Set to halt real-time clock.
+ -------X Bit 8 of days.
+
+ TODO:
+ * How are the static RAM bank outputs set when real-time clock registers
+ are selected?
+ * What happens if invalid real-time clock registers 5-7 are selected?
+ * How do invalid seconds, minutes and hours values roll over?
+ * Does MBC30 really have eight ROM bank outputs? The one game using it
+ only uses seven.
+
+ ***************************************************************************/
+
+#include "emu.h"
+#include "mbc3.h"
+
+#include "cartbase.ipp"
+#include "cartheader.h"
+#include "gbxfile.h"
+
+#include "dirtc.h"
+
+#include "corestr.h"
+
+#include <string>
+
+//#define VERBOSE 1
+//#define LOG_OUTPUT_FUNC osd_printf_info
+#include "logmacro.h"
+
+
+namespace bus::gameboy {
+
+namespace {
+
+//**************************************************************************
+// Class declarations
+//**************************************************************************
+
+class mbc3_device_base : public mbc_ram_device_base<mbc_dual_device_base>, public device_rtc_interface, public device_nvram_interface
+{
+protected:
+ mbc3_device_base(machine_config const &mconfig, device_type type, char const *tag, device_t *owner, u32 clock);
+
+ virtual void device_start() override ATTR_COLD;
+ virtual void device_reset() override ATTR_COLD;
+
+ virtual void rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second) override ATTR_COLD;
+
+ virtual void nvram_default() override ATTR_COLD;
+ virtual bool nvram_read(util::read_stream &file) override ATTR_COLD;
+ virtual bool nvram_write(util::write_stream &file) override ATTR_COLD;
+ virtual bool nvram_can_write() const override ATTR_COLD;
+
+ bool install_memory(std::string &message, unsigned highbits, unsigned lowbits) ATTR_COLD;
+
+private:
+ static inline constexpr u8 RTC_MASK[]{ 0x3f, 0x3f, 0x1f, 0xff, 0xc1 };
+ static inline constexpr u8 RTC_ROLLOVER[]{ 0x3c, 0x3c, 0x18, 0x00, 0x00 };
+
+ TIMER_CALLBACK_MEMBER(rtc_advance_seconds);
+
+ void enable_ram_rtc(u8 data);
+ void bank_switch_fine(u8 data);
+ void select_ram_rtc(u8 data);
+ void latch_rtc(u8 data);
+ u8 read_rtc(address_space &space);
+ void write_rtc(u8 data);
+
+ u8 rtc_increment(unsigned index)
+ {
+ m_rtc_regs[0][index] = (m_rtc_regs[0][index] + 1) & RTC_MASK[index];
+ if (RTC_ROLLOVER[index] == (m_rtc_regs[0][index] & RTC_ROLLOVER[index]))
+ m_rtc_regs[0][index] = 0U;
+ return m_rtc_regs[0][index];
+ }
+
+ memory_view m_view_ram;
+
+ emu_timer *m_timer_rtc;
+ s64 m_machine_seconds;
+ bool m_has_rtc_xtal;
+ bool m_has_battery;
+
+ u8 m_rtc_regs[2][5];
+ u8 m_rtc_enable;
+ u8 m_rtc_select;
+ u8 m_rtc_latch;
+};
+
+
+class mbc3_device : public mbc3_device_base
+{
+public:
+ mbc3_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
+
+ virtual image_init_result load(std::string &message) override ATTR_COLD;
+};
+
+
+class mbc30_device : public mbc3_device_base
+{
+public:
+ mbc30_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
+
+ virtual image_init_result load(std::string &message) override ATTR_COLD;
+};
+
+
+
+//**************************************************************************
+// mbc3_device_base
+//**************************************************************************
+
+mbc3_device_base::mbc3_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),
+ device_rtc_interface(mconfig, *this),
+ device_nvram_interface(mconfig, *this),
+ m_view_ram(*this, "ram"),
+ m_timer_rtc(nullptr),
+ m_machine_seconds(0),
+ m_has_rtc_xtal(false),
+ m_has_battery(false),
+ m_rtc_enable(0U),
+ m_rtc_select(0U),
+ m_rtc_latch(0U)
+{
+}
+
+
+bool mbc3_device_base::install_memory(
+ std::string &message,
+ unsigned highbits,
+ unsigned lowbits)
+{
+ // check for RTC oscillator and backup battery
+ if (loaded_through_softlist())
+ {
+ // there's a feature tag indicating presence or absence of RTC crystal
+ char const *const rtcfeature(get_feature("rtc"));
+ if (rtcfeature)
+ {
+ // explicitly specified in software list
+ if (util::streqlower(rtcfeature, "yes") || util::streqlower(rtcfeature, "true"))
+ {
+ logerror("Real-time clock crystal present\n");
+ m_has_rtc_xtal = true;
+ }
+ else if (util::streqlower(rtcfeature, "no") || util::streqlower(rtcfeature, "false"))
+ {
+ logerror("No real-time clock crystal present\n");
+ m_has_rtc_xtal = false;
+ }
+ else
+ {
+ message = "Invalid 'rtc' feature value (must be yes or no)";
+ return false;
+ }
+ }
+ else
+ {
+ logerror("No 'rtc' feature found, assuming no real-time clock crystal present\n");
+ m_has_rtc_xtal = false;
+ }
+
+ // if there's an NVRAM region, there must be a backup battery
+ if (cart_nvram_region())
+ {
+ logerror("Found 'nvram' region, backup battery must be present\n");
+ m_has_battery = true;
+ }
+ else
+ {
+ logerror("No 'nvram' region found, assuming no backup battery present\n");
+ m_has_battery = true;
+ }
+ }
+ else
+ {
+ gbxfile::leader_1_0 leader;
+ u8 const *extra;
+ u32 extralen;
+ if (gbxfile::get_data(gbx_footer_region(), leader, extra, extralen))
+ {
+ m_has_rtc_xtal = bool(leader.rtc);
+ m_has_battery = bool(leader.batt);
+ logerror(
+ "GBX format image specifies %sreal-time clock crystal present, %sbackup battery present\n",
+ m_has_rtc_xtal ? "" : "no ",
+ m_has_battery ? "" : "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_MBC3_RTC_BATT:
+ case cartheader::TYPE_MBC3_RTC_RAM_BATT:
+ m_has_rtc_xtal = true;
+ m_has_battery = true;
+ break;
+ case cartheader::TYPE_MBC3:
+ m_has_rtc_xtal = false;
+ m_has_battery = false;
+ break;
+ case cartheader::TYPE_MBC3_RAM:
+ case cartheader::TYPE_MBC3_RAM_BATT:
+ m_has_rtc_xtal = false;
+ m_has_battery = true;
+ break;
+ default:
+ osd_printf_warning(
+ "[%s] Unrecognized cartridge type 0x%02X in header, assuming no real-time clock crystal or backup battery present\n",
+ tag(),
+ carttype);
+ m_has_rtc_xtal = false;
+ m_has_battery = false;
+ }
+ logerror(
+ "Cartridge type 0x%02X in header, %sreal-time clock crystal present, %sbackup battery present\n",
+ carttype,
+ m_has_rtc_xtal ? "" : "no ",
+ m_has_battery ? "" : "no ");
+ }
+ }
+ }
+
+ // 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 false;
+
+ // set up ROM and RAM
+ cart_space()->install_view(0xa000, 0xbfff, m_view_ram);
+ install_rom();
+ install_ram(m_view_ram[0]);
+
+ // install bank switching handlers
+ cart_space()->install_write_handler(
+ 0x0000, 0x1fff,
+ write8smo_delegate(*this, FUNC(mbc3_device_base::enable_ram_rtc)));
+ cart_space()->install_write_handler(
+ 0x2000, 0x3fff,
+ write8smo_delegate(*this, FUNC(mbc3_device_base::bank_switch_fine)));
+ cart_space()->install_write_handler(
+ 0x4000, 0x5fff,
+ write8smo_delegate(*this, FUNC(mbc3_device_base::select_ram_rtc)));
+ cart_space()->install_write_handler(
+ 0x6000, 0x7fff,
+ write8smo_delegate(*this, FUNC(mbc3_device_base::latch_rtc)));
+
+ // install real-time clock handlers
+ m_view_ram[1].install_read_handler(
+ 0xa000, 0xbfff,
+ read8mo_delegate(*this, FUNC(mbc3_device_base::read_rtc)));
+ m_view_ram[1].install_write_handler(
+ 0xa000, 0xbfff,
+ write8smo_delegate(*this, FUNC(mbc3_device_base::write_rtc)));
+
+ // if real-time clock crystal is present, start it ticking
+ if (m_has_rtc_xtal)
+ {
+ logerror("Real-time clock crystal present, starting timer\n");
+ m_timer_rtc->adjust(attotime(1, 0), 0, attotime(1, 0));
+ }
+
+ // all good
+ return true;
+}
+
+
+void mbc3_device_base::device_start()
+{
+ mbc_ram_device_base<mbc_dual_device_base>::device_start();
+
+ m_timer_rtc = timer_alloc(FUNC(mbc3_device_base::rtc_advance_seconds), this);
+
+ save_item(NAME(m_rtc_regs));
+ save_item(NAME(m_rtc_enable));
+ save_item(NAME(m_rtc_select));
+ save_item(NAME(m_rtc_latch));
+}
+
+
+void mbc3_device_base::device_reset()
+{
+ mbc_ram_device_base<mbc_dual_device_base>::device_reset();
+
+ m_rtc_enable = 0U;
+ m_rtc_select = 0U;
+ m_rtc_latch = 0U;
+
+ set_bank_rom_coarse(0);
+ set_bank_rom_fine(1);
+ set_bank_ram(0);
+ m_view_ram.disable();
+}
+
+
+void mbc3_device_base::rtc_clock_updated(
+ int year,
+ int month,
+ int day,
+ int day_of_week,
+ int hour,
+ int minute,
+ int second)
+{
+ if (!m_has_rtc_xtal && !m_has_battery)
+ {
+ logerror("No real-time clock crystal or no battery present, not updating for elapsed time\n");
+ }
+ else if (std::numeric_limits<s64>::min() == m_machine_seconds)
+ {
+ logerror("Failed to load machine time from previous session, not updating for elapsed time\n");
+ }
+ else if (BIT(m_rtc_regs[0][4], 6))
+ {
+ logerror("Real-time clock halted, not updating for elapsed time\n");
+ }
+ else
+ {
+ // do a simple seconds elapsed since last run calculation
+ system_time current;
+ machine().current_datetime(current);
+ s64 delta(std::make_signed_t<decltype(current.time)>(current.time) - m_machine_seconds);
+ logerror("Previous session time, %d current time %d, delta %d\n", current.time, m_machine_seconds, delta);
+ if (0 > delta)
+ {
+ // This happens if the user runs the emulation faster
+ // than real time, exits, and then starts again without
+ // waiting for the difference between emulated and real
+ // time to elapse.
+ logerror("Previous session ended in the future, not updating for elapsed time\n");
+ }
+ else
+ {
+ logerror(
+ "Time before applying delta %u %02u:%02u:%02u%s\n",
+ (u16(BIT(m_rtc_regs[0][4], 0)) << 8) | m_rtc_regs[0][3],
+ m_rtc_regs[0][2],
+ m_rtc_regs[0][1],
+ m_rtc_regs[0][0],
+ BIT(m_rtc_regs[0][4], 7) ? " (overflow)" : "");
+
+ // annoyingly, we can get two rollovers if we started with an invalid value
+ unsigned seconds(delta % 60);
+ delta /= 60;
+ if (60 <= m_rtc_regs[0][0])
+ {
+ m_rtc_regs[0][0] = 0U;
+ --seconds;
+ ++delta;
+ }
+ if (60 <= (m_rtc_regs[0][0] + seconds))
+ ++delta;
+ m_rtc_regs[0][0] = (m_rtc_regs[0][0] + seconds) % 60;
+
+ // minutes is the same
+ unsigned minutes(delta % 60);
+ delta /= 60;
+ if (60 <= m_rtc_regs[0][1])
+ {
+ m_rtc_regs[0][1] = 0U;
+ --minutes;
+ ++delta;
+ }
+ if (60 <= (m_rtc_regs[0][1] + minutes))
+ ++delta;
+ m_rtc_regs[0][1] = (m_rtc_regs[0][1] + minutes) % 60;
+
+ // hours just has a different rollover point
+ unsigned hours(delta % 24);
+ delta /= 24;
+ if (24 <= m_rtc_regs[0][2])
+ {
+ m_rtc_regs[0][2] = 0U;
+ --hours;
+ ++delta;
+ }
+ if (24 <= (m_rtc_regs[0][2] + hours))
+ ++delta;
+ m_rtc_regs[0][2] = (m_rtc_regs[0][2] + hours) % 24;
+
+ // days has simple binary rollover
+ unsigned days(delta % 256);
+ if (256 <= (m_rtc_regs[0][3] + days))
+ ++delta;
+ m_rtc_regs[0][3] += days;
+
+ // set overflow flag if appropriate
+ if ((1 < delta) || (BIT(m_rtc_regs[0][4], 0) && delta))
+ m_rtc_regs[0][4] |= 0x80;
+ m_rtc_regs[0][4] ^= BIT(delta, 0);
+
+ logerror(
+ "Time after applying delta %u %02u:%02u:%02u%s\n",
+ (u16(BIT(m_rtc_regs[0][4], 0)) << 8) | m_rtc_regs[0][3],
+ m_rtc_regs[0][2],
+ m_rtc_regs[0][1],
+ m_rtc_regs[0][0],
+ BIT(m_rtc_regs[0][4], 7) ? " (overflow)" : "");
+ }
+ }
+}
+
+
+void mbc3_device_base::nvram_default()
+{
+ // TODO: proper cold RTC state
+ m_machine_seconds = std::numeric_limits<s64>::min();
+ for (unsigned i = 0U; std::size(m_rtc_regs[0]) > i; ++i)
+ m_rtc_regs[0][i] = RTC_MASK[i];
+}
+
+
+bool mbc3_device_base::nvram_read(util::read_stream &file)
+{
+ if (m_has_battery)
+ {
+ // read previous machine time (seconds since epoch) and RTC registers
+ u64 seconds;
+ std::size_t actual;
+ if (file.read(&seconds, sizeof(seconds), actual) || (sizeof(seconds) != actual))
+ return false;
+ m_machine_seconds = big_endianize_int64(seconds);
+
+ if (file.read(&m_rtc_regs[0][0], sizeof(m_rtc_regs[0]), actual) || (sizeof(m_rtc_regs[0]) != actual))
+ return false;
+ }
+ else
+ {
+ logerror("No battery present, not loading real-time clock register contents\n");
+ }
+ return true;
+}
+
+
+bool mbc3_device_base::nvram_write(util::write_stream &file)
+{
+ // save current machine time as seconds since epoch and RTC registers
+ system_time current;
+ machine().current_datetime(current);
+ u64 const seconds(big_endianize_int64(s64(std::make_signed_t<decltype(current.time)>(current.time))));
+ std::size_t written;
+ if (file.write(&seconds, sizeof(seconds), written) || (sizeof(seconds) != written))
+ return false;
+ if (file.write(&m_rtc_regs[0][0], sizeof(m_rtc_regs[0]), written) || (sizeof(m_rtc_regs[0]) != written))
+ return false;
+ return true;
+}
+
+
+bool mbc3_device_base::nvram_can_write() const
+{
+ return m_has_battery;
+}
+
+
+TIMER_CALLBACK_MEMBER(mbc3_device_base::rtc_advance_seconds)
+{
+ if (BIT(m_rtc_regs[0][4], 6))
+ return;
+
+ if (rtc_increment(0))
+ return;
+ if (rtc_increment(1))
+ return;
+ if (rtc_increment(2))
+ return;
+ if (++m_rtc_regs[0][3])
+ return;
+
+ if (BIT(m_rtc_regs[0][4], 0))
+ {
+ LOG("Day counter overflow");
+ m_rtc_regs[0][4] |= 0x80;
+ }
+ m_rtc_regs[0][4] ^= 0x01;
+}
+
+
+void mbc3_device_base::enable_ram_rtc(u8 data)
+{
+ m_rtc_enable = (0x0a == (data & 0x0f)) ? 1U : 0U;
+ if (!m_rtc_enable)
+ {
+ LOG(
+ "%s: Cartridge RAM and RTC registers disabled\n",
+ machine().describe_context());
+ m_view_ram.disable();
+ }
+ else if (BIT(m_rtc_select, 3))
+ {
+ LOG(
+ "%s: RTC register %u enabled\n",
+ machine().describe_context(),
+ m_rtc_select & 0x07);
+ m_view_ram.select(1);
+ }
+ else
+ {
+ LOG("%s: Cartridge RAM enabled\n", machine().describe_context());
+ m_view_ram.select(0);
+ }
+}
+
+
+void mbc3_device_base::bank_switch_fine(u8 data)
+{
+ data &= 0x7f;
+ set_bank_rom_fine(data ? data : 1);
+}
+
+
+void mbc3_device_base::select_ram_rtc(u8 data)
+{
+ // TODO: what happens with the RAM bank outputs when the RTC is selected?
+ // TODO: what happens if RTC register 5-7 is selected?
+ // TODO: is the high nybble ignored altogether?
+ set_bank_rom_coarse(data & 0x07);
+ set_bank_ram(data & 0x07);
+ m_rtc_select = data;
+ if (m_rtc_enable)
+ {
+ if (BIT(data, 3))
+ {
+ LOG(
+ "%s: RTC register %u enabled\n",
+ machine().describe_context(),
+ data & 0x07);
+ m_view_ram.select(1);
+ }
+ else
+ {
+ LOG("%s: Cartridge RAM enabled\n", machine().describe_context());
+ m_view_ram.select(0);
+ }
+ }
+}
+
+
+void mbc3_device_base::latch_rtc(u8 data)
+{
+ // FIXME: does it just check the least significant bit, or does it look for 0x00 and 0x01?
+ LOG("Latch RTC 0x%02X -> 0x%02X\n", m_rtc_latch, data);
+ if (!BIT(m_rtc_latch, 0) && BIT(data, 0))
+ {
+ LOG("%s: Latching RTC registers\n", machine().describe_context());
+ std::copy(std::begin(m_rtc_regs[0]), std::end(m_rtc_regs[0]), std::begin(m_rtc_regs[1]));
+ }
+ m_rtc_latch = data;
+}
+
+
+u8 mbc3_device_base::read_rtc(address_space &space)
+{
+ u8 const reg(m_rtc_select & 0x07);
+ if (std::size(m_rtc_regs[1]) > reg)
+ {
+ LOG(
+ "%s: Read RTC register %u = 0x%02X\n",
+ machine().describe_context(),
+ reg,
+ m_rtc_regs[1][reg]);
+ return m_rtc_regs[1][reg];
+ }
+ else
+ {
+ LOG(
+ "%s: Read invalid RTC register %u\n",
+ machine().describe_context(),
+ reg);
+ return space.unmap();
+ }
+}
+
+
+void mbc3_device_base::write_rtc(u8 data)
+{
+ u8 const reg(m_rtc_select & 0x07);
+ if (std::size(m_rtc_regs[0]) > reg)
+ {
+ LOG(
+ "%s: Write RTC register %u = 0x%02X\n",
+ machine().describe_context(),
+ reg,
+ data);
+ if (4U == reg)
+ {
+ // TODO: are bits 5-1 physically present, and if not, what do they read as?
+ // TODO: how does halting the RTC interact with the prescaler?
+ data &= 0xc1;
+ m_rtc_regs[0][reg] = data;
+ }
+ else
+ {
+ m_rtc_regs[0][reg] = data;
+ }
+ }
+ else
+ {
+ LOG(
+ "%s: Write invalid RTC register %u = 0x%02X\n",
+ machine().describe_context(),
+ reg,
+ data);
+ }
+}
+
+
+
+//**************************************************************************
+// mbc3_device
+//**************************************************************************
+
+mbc3_device::mbc3_device(
+ machine_config const &mconfig,
+ char const *tag,
+ device_t *owner,
+ u32 clock) :
+ mbc3_device_base(mconfig, GB_ROM_MBC3, tag, owner, clock)
+{
+}
+
+
+image_init_result mbc3_device::load(std::string &message)
+{
+ if (!install_memory(message, 2, 7))
+ return image_init_result::FAIL;
+ else
+ return image_init_result::PASS;
+}
+
+
+
+//**************************************************************************
+// mbc30_device
+//**************************************************************************
+
+mbc30_device::mbc30_device(
+ machine_config const &mconfig,
+ char const *tag,
+ device_t *owner,
+ u32 clock) :
+ mbc3_device_base(mconfig, GB_ROM_MBC30, tag, owner, clock)
+{
+}
+
+
+image_init_result mbc30_device::load(std::string &message)
+{
+ if (!install_memory(message, 3, 8))
+ return image_init_result::FAIL;
+ else
+ return image_init_result::PASS;
+}
+
+} // anonymous namespace
+
+} // namespace bus::gameboy
+
+
+DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_MBC3, device_gb_cart_interface, bus::gameboy::mbc3_device, "gb_rom_mbc3", "Game Boy MBC3 Cartridge")
+DEFINE_DEVICE_TYPE_PRIVATE(GB_ROM_MBC30, device_gb_cart_interface, bus::gameboy::mbc30_device, "gb_rom_mbc30", "Game Boy MBC30 Cartridge")
diff --git a/src/devices/bus/gameboy/mbc3.h b/src/devices/bus/gameboy/mbc3.h
new file mode 100644
index 00000000000..e039262fc3d
--- /dev/null
+++ b/src/devices/bus/gameboy/mbc3.h
@@ -0,0 +1,19 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ Nintendo Game Boy Memory Bank Controller 3
+
+ ***************************************************************************/
+#ifndef MAME_BUS_GAMEBOY_MBC3_H
+#define MAME_BUS_GAMEBOY_MBC3_H
+
+#pragma once
+
+#include "slot.h"
+
+
+DECLARE_DEVICE_TYPE(GB_ROM_MBC3, device_gb_cart_interface)
+DECLARE_DEVICE_TYPE(GB_ROM_MBC30, device_gb_cart_interface)
+
+#endif // MAME_BUS_GAMEBOY_MBC3_H
diff --git a/src/devices/cpu/mn1880/mn1880.cpp b/src/devices/cpu/mn1880/mn1880.cpp
index 0a23b988918..0843947a480 100644
--- a/src/devices/cpu/mn1880/mn1880.cpp
+++ b/src/devices/cpu/mn1880/mn1880.cpp
@@ -66,7 +66,7 @@
incorrectly reported for writes.
* Due to the pipelining of writes, an interrupt may be accepted during
an instruction which attempts to disable interrupts by setting the
- IEMASK bit, which may be cleared insted when control reaches the next
+ IEMASK bit, which may be cleared instead when control reaches the next
sequential instruction from a RETI. This sequencing glitch is
documented in the MN187XX23 user's manual, along with a failsafe way
of setting IEMASK, and similar workarounds in extant program code
diff --git a/src/mame/atari/atarigt.cpp b/src/mame/atari/atarigt.cpp
index 7058f25cb05..44a72cdd6fe 100644
--- a/src/mame/atari/atarigt.cpp
+++ b/src/mame/atari/atarigt.cpp
@@ -1194,7 +1194,7 @@ ROM_START( tmek20 )
ROM_END
-ROM_START( primrage ) // still shows 'version 2.3' on the title screen but build is newer than the primrage set
+ROM_START( primrage ) // still shows 'version 2.3' on the title screen but build is newer than the primrageo set
ROM_REGION( 0x200000, "maincpu", 0 ) /* 8*64k for 68000 code, differ from the primrageo set */
ROM_LOAD32_BYTE( "rage_136102-2044a_pgmuu.29l", 0x000000, 0x80000, CRC(85556b91) SHA1(5f4f5d0bf68bd17b7bff230b521a5dcfff414a50) )
ROM_LOAD32_BYTE( "rage_136102-2043a_pgmum.28l", 0x000001, 0x80000, CRC(4d3414d0) SHA1(b6465c0fbee4e67f74185e9ea048e40f4f443efa) )
diff --git a/src/mame/jaleco/armchamp.cpp b/src/mame/jaleco/armchamp.cpp
index 08f4fc38550..961df09c929 100644
--- a/src/mame/jaleco/armchamp.cpp
+++ b/src/mame/jaleco/armchamp.cpp
@@ -54,6 +54,8 @@ protected:
virtual void video_start() override;
private:
+ static constexpr XTAL MASTER_CLOCK = 12_MHz_XTAL;
+
uint32_t screen_update_armchamp(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
void main_prg(address_map &map);
@@ -87,8 +89,6 @@ private:
uint8_t m_adpcm_byte = 0;
uint8_t m_msm5205_vclk_toggle = 0;
uint16_t m_paletteram[0x100] = { };
-
- static constexpr XTAL MASTER_CLOCK = 12_MHz_XTAL;
};
@@ -196,7 +196,7 @@ void armchamp_state::io1_w(uint8_t data)
// .xxx x... - Tile ROM bank
// x... .... - Speech ROM banking enable
- m_rombank->set_entry(((data & 0x80) >> 5) | (data & 3));
+ m_rombank->set_entry(bitswap<3>(data, 7, 1, 0));
if ((m_io1 ^ data) & 0x78)
{