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]
This commit is contained in:
parent
a272716bce
commit
f4b23f1737
@ -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. -->
|
||||
|
@ -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>
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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];
|
||||
@ -566,508 +526,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")
|
||||
|
@ -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)
|
||||
|
721
src/devices/bus/gameboy/mbc3.cpp
Normal file
721
src/devices/bus/gameboy/mbc3.cpp
Normal file
@ -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")
|
19
src/devices/bus/gameboy/mbc3.h
Normal file
19
src/devices/bus/gameboy/mbc3.h
Normal file
@ -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
|
@ -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
|
||||
|
@ -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) )
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user