mirror of
https://github.com/holub/mame
synced 2025-04-20 15:32:45 +03:00
bus/gameboy: Added basic HuC-3 real-time clock simulation, and cleanup.
* Added MBC30 as a distinct slot option for documentation purposes. * Added heuristics to detect MBC30 for GBX and plain ROM dump files. * mbc.cpp: Disabled noisy logging.
This commit is contained in:
parent
da7bdd575c
commit
3c49020bab
@ -15950,7 +15950,7 @@ license:CC0
|
||||
<feature name="u3" value="U3 RAM [D431000AGW-70LL]" />
|
||||
<feature name="u4" value="U4 MM1134 [6735]" />
|
||||
<feature name="battery" value="CR2025" />
|
||||
<feature name="slot" value="rom_mbc3" />
|
||||
<feature name="slot" value="rom_mbc30" />
|
||||
<feature name="rtc" value="yes" />
|
||||
<dataarea name="rom" size="2097152">
|
||||
<rom name="cgb-bxtj-0.u1" size="2097152" crc="270c4ecc" sha1="95127b901bbce2407daf43cce9f45d4c27ef635d"/>
|
||||
|
@ -37,6 +37,7 @@ char const *const GB_GBCK003 = "rom_gbck003";
|
||||
char const *const GB_MBC1 = "rom_mbc1";
|
||||
char const *const GB_MBC2 = "rom_mbc2";
|
||||
char const *const GB_MBC3 = "rom_mbc3";
|
||||
char const *const GB_MBC30 = "rom_mbc30";
|
||||
char const *const GB_MBC5 = "rom_mbc5";
|
||||
char const *const GB_MBC6 = "rom_mbc6";
|
||||
char const *const GB_MBC7_2K = "rom_mbc7_2k";
|
||||
@ -76,6 +77,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_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,6 +31,7 @@ extern char const *const GB_MBC1;
|
||||
extern char const *const GB_MBC2;
|
||||
extern char const *const GB_MBC3;
|
||||
extern char const *const GB_MBC3;
|
||||
extern char const *const GB_MBC30;
|
||||
extern char const *const GB_MBC5;
|
||||
extern char const *const GB_MBC6;
|
||||
extern char const *const GB_MBC7_2K;
|
||||
|
@ -484,6 +484,32 @@ bool is_wisdom_tree(std::string_view tag, util::random_read &file, u64 length, u
|
||||
}
|
||||
|
||||
|
||||
bool is_mbc30(std::string_view tag, util::random_read &file, u64 length, u64 offset, u8 const *header)
|
||||
{
|
||||
// MBC30 supposedly has an additional ROM bank output
|
||||
if ((u32(0x4000) << 7) < length)
|
||||
{
|
||||
osd_printf_verbose(
|
||||
"[%s] Assuming 0x%06X-byte cartridge declaring MBC3 controller uses MBC30\n",
|
||||
tag,
|
||||
length);
|
||||
return true;
|
||||
}
|
||||
|
||||
// MBC30 has three RAM bank outputs, supporting up to 64 KiB static RAM
|
||||
if (cartheader::RAM_SIZE_64K == header[cartheader::OFFSET_RAM_SIZE - 0x100])
|
||||
{
|
||||
osd_printf_verbose(
|
||||
"[%s] Assuming cartridge declaring MBC3 controller with 64 KiB RAM uses MBC30\n",
|
||||
tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
// MBC3 should be fine
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool is_m161(std::string_view tag, util::random_read &file, u64 length, u64 offset, u8 const *header)
|
||||
{
|
||||
// supports eight 32 KiB banks at most, doesn't make sense without at least two banks
|
||||
@ -667,7 +693,10 @@ std::optional<char const *> probe_gbx_footer(std::string_view tag, util::random_
|
||||
result = slotoptions::GB_MBC2;
|
||||
break;
|
||||
case gbxfile::TYPE_MBC3:
|
||||
result = slotoptions::GB_MBC3;
|
||||
if (((u32(0x4000) << 7) < leader.rom_bytes) || ((u32(0x2000) << 2) < leader.ram_bytes))
|
||||
result = slotoptions::GB_MBC30;
|
||||
else
|
||||
result = slotoptions::GB_MBC3;
|
||||
break;
|
||||
case gbxfile::TYPE_MBC5:
|
||||
result = slotoptions::GB_MBC5;
|
||||
@ -845,14 +874,20 @@ char const *guess_cart_type(std::string_view tag, util::random_read &file, u64 l
|
||||
return slotoptions::GB_MMM01;
|
||||
// 0x0e
|
||||
case cartheader::TYPE_MBC3_RTC_BATT:
|
||||
if (is_mbc30(tag, file, length, offset, header))
|
||||
return slotoptions::GB_MBC30;
|
||||
return slotoptions::GB_MBC3;
|
||||
case cartheader::TYPE_MBC3_RTC_RAM_BATT:
|
||||
if (is_m161(tag, file, length, offset, header))
|
||||
return slotoptions::GB_M161;
|
||||
else if (is_mbc30(tag, file, length, offset, header))
|
||||
return slotoptions::GB_MBC30;
|
||||
return slotoptions::GB_MBC3;
|
||||
case cartheader::TYPE_MBC3:
|
||||
case cartheader::TYPE_MBC3_RAM:
|
||||
case cartheader::TYPE_MBC3_RAM_BATT:
|
||||
if (is_mbc30(tag, file, length, offset, header))
|
||||
return slotoptions::GB_MBC30;
|
||||
return slotoptions::GB_MBC3;
|
||||
// 0x14
|
||||
case cartheader::TYPE_MBC5:
|
||||
|
@ -69,17 +69,18 @@
|
||||
when it has completed the command and is ready to execute another command.
|
||||
|
||||
Five of the eight possible commands are used by the games:
|
||||
0x1 - Read register and increment address (value put in bits 3-0 of 0xC)
|
||||
0x3 - Write register and increment address (value from bits 3-0 of 0xB)
|
||||
0x4 - Set register address low nybble
|
||||
0x5 - Set register address high nybble
|
||||
0x6 - Execute extended command (selector from bits 3-0 of 0xB)
|
||||
0x1 - Read register and increment address (value put in bits 3-0 of 0xC).
|
||||
0x3 - Write register and increment address (value from bits 3-0 of 0xB).
|
||||
0x4 - Set register address low nybble.
|
||||
0x5 - Set register address high nybble.
|
||||
0x6 - Execute extended command (selector from bits 3-0 of 0xB).
|
||||
|
||||
The games use four of the sixteen possible extended commands:
|
||||
0x0 - Atomically read real-time clock to registers 0-6
|
||||
0x1 - Atomically write real-time clock from registers 0-6
|
||||
0x2 - Some kind of handshake/status request - sets result to 0x1
|
||||
0xe - Sent twice to trigger melody generator
|
||||
0x0 - Atomically read real-time clock to registers 0x00-0x06.
|
||||
0x1 - Atomically write real-time clock from registers 0x00-0x06.
|
||||
Also updates event time in registers 0x58-0x5D.
|
||||
0x2 - Some kind of handshake/status request - sets result to 0x1.
|
||||
0xe - Sent twice to trigger melody generator.
|
||||
|
||||
Registers are likely a window into the microcontroller's memory. Known
|
||||
registers:
|
||||
@ -89,11 +90,12 @@
|
||||
0x13-15 - Day counter (least significant nybble low)
|
||||
0x26 - Bits 1-0 select melody
|
||||
0x27 - Enable (0x1) or disable (not 0x1) melody
|
||||
0x58-5A - Event time minutes (least significant nybble low)
|
||||
0x5B-5D - Event time days (least significant nybble low)
|
||||
|
||||
TODO:
|
||||
* Implement real-time clock.
|
||||
* Simulate more microcontroller functionality as it's discovered.
|
||||
* Simulate melody generator?
|
||||
* Which of the internal registers are battery-backed?
|
||||
* What is the default state for banking and infrared select on reset?
|
||||
* Does ROM bank 0 map to bank 1 like MBC1?
|
||||
* How many RAM page lines are there? No games use more than 2.
|
||||
@ -104,10 +106,16 @@
|
||||
#include "huc3.h"
|
||||
|
||||
#include "cartbase.ipp"
|
||||
#include "gbxfile.h"
|
||||
|
||||
#include "dirtc.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
//#define VERBOSE 1
|
||||
//#define LOG_OUTPUT_FUNC osd_printf_info
|
||||
@ -118,7 +126,10 @@ namespace bus::gameboy {
|
||||
|
||||
namespace {
|
||||
|
||||
class huc3_device : public mbc_ram_device_base<mbc_dual_device_base>
|
||||
class huc3_device :
|
||||
public mbc_ram_device_base<mbc_dual_device_base>,
|
||||
public device_rtc_interface,
|
||||
public device_nvram_interface
|
||||
{
|
||||
public:
|
||||
static constexpr feature_type unemulated_features() { return feature::SOUND | feature::COMMS; }
|
||||
@ -130,6 +141,13 @@ protected:
|
||||
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;
|
||||
|
||||
private:
|
||||
void io_select(u8 data);
|
||||
void bank_switch_fine(u8 data);
|
||||
@ -141,6 +159,8 @@ private:
|
||||
u8 read_ir(address_space &space);
|
||||
void write_ir(u8 data);
|
||||
|
||||
TIMER_CALLBACK_MEMBER(rtc_advance_seconds);
|
||||
|
||||
void execute_instruction()
|
||||
{
|
||||
switch (m_ctrl_data & 0x0f)
|
||||
@ -150,8 +170,35 @@ private:
|
||||
std::copy_n(&m_registers[0x10], 7, &m_registers[0x00]);
|
||||
break;
|
||||
case 0x1:
|
||||
LOG("Instruction 0x2 - atomic RTC write\n");
|
||||
std::copy_n(&m_registers[0x00], 7, &m_registers[0x10]);
|
||||
{
|
||||
LOG("Instruction 0x2 - atomic RTC write\n");
|
||||
|
||||
s16 const newminutes(read_12bit(0x00));
|
||||
s16 const newdays(read_12bit(0x03));
|
||||
s16 const oldminutes(read_12bit(0x10));
|
||||
s16 const olddays(read_12bit(0x13));
|
||||
s16 const eventminutes(read_12bit(0x58));
|
||||
s16 const eventdays(read_12bit(0x5b));
|
||||
|
||||
s16 minutesdelta(newminutes - oldminutes);
|
||||
s16 daysdelta(newdays - olddays);
|
||||
while ((60 * 24) <= (eventminutes + minutesdelta))
|
||||
{
|
||||
minutesdelta -= 60 * 24;
|
||||
++daysdelta;
|
||||
}
|
||||
while (0 > (eventminutes + minutesdelta))
|
||||
{
|
||||
minutesdelta += 60 * 24;
|
||||
--daysdelta;
|
||||
}
|
||||
|
||||
assert(0 <= (eventminutes + minutesdelta));
|
||||
assert((60 * 24) > (eventminutes + minutesdelta));
|
||||
std::copy_n(&m_registers[0x00], 7, &m_registers[0x10]);
|
||||
write_12bit(0x58, s16(eventminutes + minutesdelta));
|
||||
write_12bit(0x5b, s16(eventdays + daysdelta));
|
||||
}
|
||||
break;
|
||||
case 0x2:
|
||||
logerror("Instruction 0x2 - setting data to 0x1\n");
|
||||
@ -168,8 +215,27 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
memory_view m_view_io;
|
||||
u16 read_12bit(u8 offset) const
|
||||
{
|
||||
return
|
||||
(u16(m_registers[(offset + 0) & 0xff] & 0x0f) << 0) |
|
||||
(u16(m_registers[(offset + 1) & 0xff] & 0x0f) << 4) |
|
||||
(u16(m_registers[(offset + 2) & 0xff] & 0x0f) << 8);
|
||||
}
|
||||
|
||||
void write_12bit(u8 offset, u16 data)
|
||||
{
|
||||
m_registers[(offset + 0) & 0xff] = (data >> 0) & 0x0f;
|
||||
m_registers[(offset + 1) & 0xff] = (data >> 4) & 0x0f;
|
||||
m_registers[(offset + 2) & 0xff] = (data >> 8) & 0x0f;
|
||||
}
|
||||
|
||||
memory_view m_view_io;
|
||||
emu_timer *m_timer_rtc;
|
||||
s64 m_machine_seconds;
|
||||
bool m_has_battery;
|
||||
|
||||
u8 m_seconds;
|
||||
u8 m_ctrl_cmd;
|
||||
u8 m_ctrl_data;
|
||||
u8 m_ctrl_addr;
|
||||
@ -183,7 +249,13 @@ huc3_device::huc3_device(
|
||||
device_t *owner,
|
||||
u32 clock) :
|
||||
mbc_ram_device_base<mbc_dual_device_base>(mconfig, GB_ROM_HUC3, tag, owner, clock),
|
||||
device_rtc_interface(mconfig, *this),
|
||||
device_nvram_interface(mconfig, *this),
|
||||
m_view_io(*this, "io"),
|
||||
m_timer_rtc(nullptr),
|
||||
m_machine_seconds(0),
|
||||
m_has_battery(false),
|
||||
m_seconds(0U),
|
||||
m_ctrl_cmd(0U),
|
||||
m_ctrl_data(0U),
|
||||
m_ctrl_addr(0U)
|
||||
@ -193,6 +265,41 @@ huc3_device::huc3_device(
|
||||
|
||||
image_init_result huc3_device::load(std::string &message)
|
||||
{
|
||||
// check for backup battery
|
||||
if (loaded_through_softlist())
|
||||
{
|
||||
// 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_battery = bool(leader.batt);
|
||||
logerror(
|
||||
"GBX format image specifies %sbackup battery present\n",
|
||||
m_has_battery ? "" : "no ");
|
||||
}
|
||||
else
|
||||
{
|
||||
// just assume the coin cell is present - every known game has it
|
||||
logerror("Assuming backup battery present\n");
|
||||
m_has_battery = true;
|
||||
}
|
||||
}
|
||||
|
||||
// check for valid ROM/RAM regions
|
||||
set_bank_bits_rom(2, 7);
|
||||
set_bank_bits_ram(2);
|
||||
@ -244,12 +351,18 @@ void huc3_device::device_start()
|
||||
{
|
||||
mbc_ram_device_base<mbc_dual_device_base>::device_start();
|
||||
|
||||
m_seconds = 0U;
|
||||
std::fill(std::begin(m_registers), std::end(m_registers), 0U);
|
||||
|
||||
m_timer_rtc = timer_alloc(FUNC(huc3_device::rtc_advance_seconds), this);
|
||||
|
||||
save_item(NAME(m_seconds));
|
||||
save_item(NAME(m_ctrl_cmd));
|
||||
save_item(NAME(m_ctrl_data));
|
||||
save_item(NAME(m_ctrl_addr));
|
||||
save_item(NAME(m_registers));
|
||||
|
||||
m_timer_rtc->adjust(attotime(1, 0), 0, attotime(1, 0));
|
||||
}
|
||||
|
||||
|
||||
@ -270,6 +383,149 @@ void huc3_device::device_reset()
|
||||
}
|
||||
|
||||
|
||||
void huc3_device::rtc_clock_updated(
|
||||
int year,
|
||||
int month,
|
||||
int day,
|
||||
int day_of_week,
|
||||
int hour,
|
||||
int minute,
|
||||
int second)
|
||||
{
|
||||
if (!m_has_battery)
|
||||
{
|
||||
logerror("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
|
||||
{
|
||||
// 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
|
||||
{
|
||||
// combine the counter nybbles for convenience
|
||||
u16 minutes(read_12bit(0x10));
|
||||
u16 days(read_12bit(0x13));
|
||||
logerror(
|
||||
"Time before applying delta %u %02u:%02u:%02u\n",
|
||||
days,
|
||||
minutes / 60,
|
||||
minutes % 60,
|
||||
m_seconds);
|
||||
|
||||
// deal with seconds
|
||||
unsigned s(delta % 60);
|
||||
delta /= 60;
|
||||
if (64 <= m_seconds)
|
||||
{
|
||||
m_seconds = 0U;
|
||||
--s;
|
||||
++delta;
|
||||
}
|
||||
if (60 <= (m_seconds + s))
|
||||
++delta;
|
||||
m_seconds = (m_seconds + s) % 60;
|
||||
|
||||
// update the minute counter value
|
||||
unsigned m(delta % (60 * 24));
|
||||
delta /= 60 * 24;
|
||||
if ((60 * 24) <= minutes)
|
||||
{
|
||||
minutes = 0U;
|
||||
--m;
|
||||
++delta;
|
||||
}
|
||||
if ((60 * 24) <= (minutes + m))
|
||||
++delta;
|
||||
minutes = (minutes + m) % (60 * 24);
|
||||
|
||||
// no special handling for day counter
|
||||
days += delta;
|
||||
|
||||
// write the counter nybbles back to registers
|
||||
write_12bit(0x10, minutes);
|
||||
write_12bit(0x13, days);
|
||||
logerror(
|
||||
"Time after applying delta %u %02u:%02u:%02u\n",
|
||||
days,
|
||||
minutes / 60,
|
||||
minutes % 60,
|
||||
m_seconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void huc3_device::nvram_default()
|
||||
{
|
||||
// TODO: proper cold RTC state
|
||||
m_machine_seconds = std::numeric_limits<s64>::min();
|
||||
m_seconds = 0U;
|
||||
std::fill(std::begin(m_registers), std::end(m_registers), 0U);
|
||||
}
|
||||
|
||||
|
||||
bool huc3_device::nvram_read(util::read_stream &file)
|
||||
{
|
||||
if (m_has_battery)
|
||||
{
|
||||
// read previous machine time (seconds since epoch), seconds counter, and register contents
|
||||
u64 machinesecs;
|
||||
std::size_t actual;
|
||||
if (file.read(&machinesecs, sizeof(machinesecs), actual) || (sizeof(machinesecs) != actual))
|
||||
return false;
|
||||
m_machine_seconds = big_endianize_int64(machinesecs);
|
||||
|
||||
if (file.read(&m_seconds, sizeof(m_seconds), actual) || (sizeof(m_seconds) != actual))
|
||||
return false;
|
||||
if (file.read(&m_registers[0], sizeof(m_registers), actual) || (sizeof(m_registers) != actual))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
logerror("No battery present, not loading real-time clock register contents\n");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool huc3_device::nvram_write(util::write_stream &file)
|
||||
{
|
||||
// save current machine time as seconds since epoch, seconds counter, and register contents
|
||||
system_time current;
|
||||
machine().current_datetime(current);
|
||||
u64 const machinesecs(big_endianize_int64(s64(std::make_signed_t<decltype(current.time)>(current.time))));
|
||||
std::size_t written;
|
||||
if (file.write(&machinesecs, sizeof(machinesecs), written) || (sizeof(machinesecs) != written))
|
||||
return false;
|
||||
if (file.write(&m_seconds, sizeof(m_seconds), written) || (sizeof(m_seconds) != written))
|
||||
return false;
|
||||
if (file.write(&m_registers[0], sizeof(m_registers), written) || (sizeof(m_registers) != written))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool huc3_device::nvram_can_write() const
|
||||
{
|
||||
return m_has_battery;
|
||||
}
|
||||
|
||||
|
||||
void huc3_device::io_select(u8 data)
|
||||
{
|
||||
switch (data & 0x0f)
|
||||
@ -402,6 +658,27 @@ void huc3_device::write_ir(u8 data)
|
||||
LOG("%s: Infrared write 0x%02X\n", machine().describe_context(), data);
|
||||
}
|
||||
|
||||
|
||||
TIMER_CALLBACK_MEMBER(huc3_device::rtc_advance_seconds)
|
||||
{
|
||||
if ((60 - 1) > m_seconds)
|
||||
{
|
||||
++m_seconds;
|
||||
return;
|
||||
}
|
||||
|
||||
m_seconds = 0U;
|
||||
u16 const minutes(read_12bit(0x10));
|
||||
if (((60 * 24) - 1) > minutes)
|
||||
{
|
||||
write_12bit(0x10, minutes + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
write_12bit(0x10, 0);
|
||||
write_12bit(0x13, read_12bit(0x13) + 1);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
} // namespace bus::gameboy
|
||||
|
@ -105,8 +105,8 @@
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
#define VERBOSE 1
|
||||
#define LOG_OUTPUT_FUNC osd_printf_info
|
||||
//#define VERBOSE 1
|
||||
//#define LOG_OUTPUT_FUNC osd_printf_info
|
||||
#include "logmacro.h"
|
||||
|
||||
|
||||
@ -754,7 +754,7 @@ protected:
|
||||
int day_of_week,
|
||||
int hour,
|
||||
int minute,
|
||||
int second) override
|
||||
int second) override ATTR_COLD
|
||||
{
|
||||
if (!m_has_rtc_xtal && !m_has_battery)
|
||||
{
|
||||
@ -960,7 +960,7 @@ private:
|
||||
// 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 & 0x03);
|
||||
bank_switch_coarse(data & 0x07);
|
||||
m_rtc_select = data;
|
||||
if (m_rtc_enable)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user