mirror of
https://github.com/holub/mame
synced 2025-06-01 18:41:47 +03:00
1082 lines
27 KiB
C++
1082 lines
27 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:R. Belmont,Ryan Holtz,Fabio Priuli
|
|
/***********************************************************************************************************
|
|
|
|
|
|
Game Boy Advance cart emulation
|
|
|
|
|
|
We support carts with several kind of Save RAM (actual SRAM, Flash RAM or EEPROM)
|
|
|
|
|
|
|
|
***********************************************************************************************************/
|
|
|
|
|
|
#include "emu.h"
|
|
#include "rom.h"
|
|
|
|
|
|
//-------------------------------------------------
|
|
// gba_rom_device - constructor
|
|
//-------------------------------------------------
|
|
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_STD, gba_rom_device, "gba_rom", "GBA Carts")
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_SRAM, gba_rom_sram_device, "gba_rom_sram", "GBA Carts + SRAM")
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_DRILLDOZ, gba_rom_drilldoz_device, "gba_rom_drilldoz", "GBA Carts + SRAM + Rumble")
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_WARIOTWS, gba_rom_wariotws_device, "gba_rom_wariotws", "GBA Carts + SRAM + Rumble + Gyroscope")
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_EEPROM, gba_rom_eeprom_device, "gba_rom_eeprom", "GBA Carts + EEPROM")
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_YOSHIUG, gba_rom_yoshiug_device, "gba_rom_yoshiug", "GBA Carts + EEPROM + Tilt Sensor")
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_EEPROM64, gba_rom_eeprom64_device, "gba_rom_eeprom64", "GBA Carts + EEPROM 64K")
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_BOKTAI, gba_rom_boktai_device, "gba_rom_boktai", "GBA Carts + EEPROM 64K + RTC + Light Sensor")
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_FLASH, gba_rom_flash_device, "gba_rom_flash", "GBA Carts + Panasonic Flash")
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_FLASH_RTC, gba_rom_flash_rtc_device, "gba_rom_flash_rtc", "GBA Carts + Panasonic Flash + RTC")
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_FLASH1M, gba_rom_flash1m_device, "gba_rom_flash1m", "GBA Carts + Sanyo Flash")
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_FLASH1M_RTC, gba_rom_flash1m_rtc_device, "gba_rom_flash1m_rtc", "GBA Carts + Sanyo Flash + RTC")
|
|
DEFINE_DEVICE_TYPE(GBA_ROM_3DMATRIX, gba_rom_3dmatrix_device, "gba_rom_3dmatrix", "3D Matrix Memory Mapper")
|
|
|
|
|
|
gba_rom_device::gba_rom_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
|
|
: device_t(mconfig, type, tag, owner, clock)
|
|
, device_gba_cart_interface(mconfig, *this)
|
|
{
|
|
}
|
|
|
|
gba_rom_device::gba_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_device(mconfig, GBA_ROM_STD, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
gba_rom_sram_device::gba_rom_sram_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_device(mconfig, type, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
gba_rom_sram_device::gba_rom_sram_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_sram_device(mconfig, GBA_ROM_SRAM, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
gba_rom_drilldoz_device::gba_rom_drilldoz_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_sram_device(mconfig, GBA_ROM_DRILLDOZ, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
gba_rom_wariotws_device::gba_rom_wariotws_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_sram_device(mconfig, GBA_ROM_WARIOTWS, tag, owner, clock)
|
|
, m_gyro_z(*this, "GYROZ")
|
|
{
|
|
}
|
|
|
|
gba_rom_eeprom_device::gba_rom_eeprom_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_device(mconfig, type, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
gba_rom_eeprom_device::gba_rom_eeprom_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_eeprom_device(mconfig, GBA_ROM_EEPROM, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
gba_rom_yoshiug_device::gba_rom_yoshiug_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_eeprom_device(mconfig, GBA_ROM_YOSHIUG, tag, owner, clock)
|
|
, m_tilt_x(*this, "TILTX")
|
|
, m_tilt_y(*this, "TILTY")
|
|
{
|
|
}
|
|
|
|
gba_rom_eeprom64_device::gba_rom_eeprom64_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_device(mconfig, type, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
gba_rom_eeprom64_device::gba_rom_eeprom64_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_eeprom64_device(mconfig, GBA_ROM_EEPROM64, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
gba_rom_boktai_device::gba_rom_boktai_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_eeprom64_device(mconfig, GBA_ROM_BOKTAI, tag, owner, clock)
|
|
, m_sensor(*this, "LIGHTSENSE")
|
|
{
|
|
}
|
|
|
|
gba_rom_flash_device::gba_rom_flash_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_device(mconfig, type, tag, owner, clock)
|
|
, m_flash_mask(0)
|
|
, m_flash(*this, "flash")
|
|
{
|
|
}
|
|
|
|
gba_rom_flash_device::gba_rom_flash_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_flash_device(mconfig, GBA_ROM_FLASH, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
gba_rom_flash_rtc_device::gba_rom_flash_rtc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_flash_device(mconfig, GBA_ROM_FLASH_RTC, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
gba_rom_flash1m_device::gba_rom_flash1m_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_device(mconfig, type, tag, owner, clock)
|
|
, m_flash_mask(0)
|
|
, m_flash(*this, "flash")
|
|
{
|
|
}
|
|
|
|
gba_rom_flash1m_device::gba_rom_flash1m_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_flash1m_device(mconfig, GBA_ROM_FLASH1M, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
gba_rom_flash1m_rtc_device::gba_rom_flash1m_rtc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_flash1m_device(mconfig, GBA_ROM_FLASH1M_RTC, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
gba_rom_3dmatrix_device::gba_rom_3dmatrix_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
|
: gba_rom_device(mconfig, GBA_ROM_3DMATRIX, tag, owner, clock)
|
|
{
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// mapper specific start/reset
|
|
//-------------------------------------------------
|
|
|
|
void gba_rom_device::device_start()
|
|
{
|
|
save_item(NAME(m_gpio_regs));
|
|
save_item(NAME(m_gpio_write_only));
|
|
save_item(NAME(m_gpio_dirs));
|
|
}
|
|
|
|
void gba_rom_device::device_reset()
|
|
{
|
|
m_gpio_regs[0] = 0;
|
|
m_gpio_regs[1] = 0;
|
|
m_gpio_regs[2] = 0;
|
|
m_gpio_regs[3] = 0;
|
|
m_gpio_write_only = 1;
|
|
m_gpio_dirs = 0;
|
|
}
|
|
|
|
void gba_rom_wariotws_device::device_start()
|
|
{
|
|
save_item(NAME(m_last_val));
|
|
save_item(NAME(m_counter));
|
|
}
|
|
|
|
void gba_rom_wariotws_device::device_reset()
|
|
{
|
|
m_last_val = 0;
|
|
m_counter = 0;
|
|
}
|
|
|
|
void gba_rom_flash_device::device_reset()
|
|
{
|
|
m_flash_mask = 0xffff/4;
|
|
}
|
|
|
|
void gba_rom_flash1m_device::device_reset()
|
|
{
|
|
m_flash_mask = 0x1ffff/4;
|
|
}
|
|
|
|
|
|
void gba_rom_eeprom_device::device_start()
|
|
{
|
|
// for the moment we use a custom eeprom implementation, so we alloc/save it as nvram
|
|
nvram_alloc(0x200);
|
|
m_eeprom = std::make_unique<gba_eeprom_device>(machine(), (uint8_t*)get_nvram_base(), get_nvram_size(), 6);
|
|
}
|
|
|
|
void gba_rom_yoshiug_device::device_start()
|
|
{
|
|
gba_rom_eeprom_device::device_start();
|
|
save_item(NAME(m_tilt_ready));
|
|
save_item(NAME(m_xpos));
|
|
save_item(NAME(m_ypos));
|
|
}
|
|
|
|
void gba_rom_yoshiug_device::device_reset()
|
|
{
|
|
m_tilt_ready = 0;
|
|
m_xpos = 0;
|
|
m_ypos = 0;
|
|
}
|
|
|
|
|
|
void gba_rom_eeprom64_device::device_start()
|
|
{
|
|
// for the moment we use a custom eeprom implementation, so we alloc/save it as nvram
|
|
nvram_alloc(0x2000);
|
|
m_eeprom = std::make_unique<gba_eeprom_device>(machine(), (uint8_t*)get_nvram_base(), get_nvram_size(), 14);
|
|
}
|
|
|
|
void gba_rom_boktai_device::device_start()
|
|
{
|
|
gba_rom_eeprom64_device::device_start();
|
|
m_rtc = std::make_unique<gba_s3511_device>(machine());
|
|
|
|
save_item(NAME(m_last_val));
|
|
save_item(NAME(m_counter));
|
|
}
|
|
|
|
void gba_rom_boktai_device::device_reset()
|
|
{
|
|
m_last_val = 0;
|
|
m_counter = 0;
|
|
}
|
|
|
|
void gba_rom_flash_rtc_device::device_start()
|
|
{
|
|
m_rtc = std::make_unique<gba_s3511_device>(machine());
|
|
}
|
|
|
|
void gba_rom_flash1m_rtc_device::device_start()
|
|
{
|
|
m_rtc = std::make_unique<gba_s3511_device>(machine());
|
|
}
|
|
|
|
void gba_rom_3dmatrix_device::device_start()
|
|
{
|
|
save_item(NAME(m_src));
|
|
save_item(NAME(m_dst));
|
|
save_item(NAME(m_nblock));
|
|
}
|
|
|
|
void gba_rom_3dmatrix_device::device_reset()
|
|
{
|
|
m_src = 0;
|
|
m_dst = 0;
|
|
m_nblock = 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
mapper specific handlers
|
|
-------------------------------------------------*/
|
|
|
|
/*-------------------------------------------------
|
|
This is a preliminary implementation of the
|
|
General Purpose I/O Port embedded in the GBA PCBs
|
|
as described at : http://problemkaputt.de/gbatek.htm#gbacartioportgpio
|
|
|
|
Functions read_gpio/write_gpio only give the
|
|
I/O interface while the actual on-cart devices
|
|
are read and written through gpio_dev_read/gpio_dev_write
|
|
which are virtual methods defined in the specific
|
|
cart types.
|
|
-------------------------------------------------*/
|
|
|
|
READ32_MEMBER(gba_rom_device::read_gpio)
|
|
{
|
|
if (!m_gpio_write_only)
|
|
{
|
|
switch (offset)
|
|
{
|
|
case 0:
|
|
default:
|
|
if (ACCESSING_BITS_0_15)
|
|
{
|
|
uint16_t ret = gpio_dev_read(m_gpio_dirs);
|
|
return ret;
|
|
}
|
|
if (ACCESSING_BITS_16_31)
|
|
return m_gpio_regs[1] << 16;
|
|
case 1:
|
|
if (ACCESSING_BITS_0_15)
|
|
return m_gpio_regs[2];
|
|
if (ACCESSING_BITS_16_31)
|
|
return m_gpio_regs[3] << 16;
|
|
}
|
|
return 0;
|
|
}
|
|
else
|
|
return m_rom[offset + 0xc4/4];
|
|
}
|
|
|
|
WRITE32_MEMBER(gba_rom_device::write_gpio)
|
|
{
|
|
switch (offset)
|
|
{
|
|
case 0:
|
|
default:
|
|
if (ACCESSING_BITS_0_15)
|
|
{
|
|
gpio_dev_write(data & 0xffff, m_gpio_dirs);
|
|
}
|
|
if (ACCESSING_BITS_16_31)
|
|
{
|
|
m_gpio_dirs = (data >> 16) & 0x0f;
|
|
m_gpio_regs[1] = (data >> 16) & 0xffff;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (ACCESSING_BITS_0_15)
|
|
{
|
|
m_gpio_write_only = BIT(data, 0) ? 0 : 1;
|
|
m_gpio_regs[2] = data & 0xffff;
|
|
}
|
|
if (ACCESSING_BITS_16_31)
|
|
m_gpio_regs[3] = (data >> 16) & 0xffff;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
Carts with SRAM
|
|
-------------------------------------------------*/
|
|
|
|
READ32_MEMBER(gba_rom_sram_device::read_ram)
|
|
{
|
|
if (!m_nvram.empty() && offset < m_nvram.size())
|
|
return m_nvram[offset];
|
|
else // this cannot actually happen...
|
|
return 0xffffffff;
|
|
}
|
|
|
|
WRITE32_MEMBER(gba_rom_sram_device::write_ram)
|
|
{
|
|
if (!m_nvram.empty() && offset < m_nvram.size())
|
|
COMBINE_DATA(&m_nvram[offset]);
|
|
}
|
|
|
|
|
|
// SRAM cart variant with additional Rumble motor (used by Drill Dozer)
|
|
|
|
void gba_rom_drilldoz_device::gpio_dev_write(uint16_t data, int gpio_dirs)
|
|
{
|
|
if ((gpio_dirs & 0x08))
|
|
{
|
|
// send impulse to Rumble sensor
|
|
machine().output().set_value("Rumble", BIT(data, 3));
|
|
}
|
|
}
|
|
|
|
|
|
// SRAM cart variant with additional Rumble motor + Gyroscope (used by Warioware Twist)
|
|
|
|
static INPUT_PORTS_START( wariotws_gyroscope )
|
|
PORT_START("GYROZ")
|
|
PORT_BIT( 0xfff, 0x6c0, IPT_AD_STICK_Z ) PORT_MINMAX(0x354,0x9e3) PORT_SENSITIVITY(0x10) PORT_KEYDELTA(0x50)
|
|
INPUT_PORTS_END
|
|
|
|
ioport_constructor gba_rom_wariotws_device::device_input_ports() const
|
|
{
|
|
return INPUT_PORTS_NAME( wariotws_gyroscope );
|
|
}
|
|
|
|
uint16_t gba_rom_wariotws_device::gpio_dev_read(int gpio_dirs)
|
|
{
|
|
int gyro = 0;
|
|
if (gpio_dirs == 0x0b)
|
|
gyro = BIT(m_gyro_z->read(), m_counter);
|
|
return (gyro << 2);
|
|
}
|
|
|
|
void gba_rom_wariotws_device::gpio_dev_write(uint16_t data, int gpio_dirs)
|
|
{
|
|
if ((gpio_dirs & 0x08))
|
|
{
|
|
// send impulse to Rumble sensor
|
|
machine().output().set_value("Rumble", BIT(data, 3));
|
|
}
|
|
|
|
if (gpio_dirs == 0x0b)
|
|
{
|
|
if ((data & 2) && (m_counter > 0))
|
|
m_counter--;
|
|
|
|
if (data & 1)
|
|
m_counter = 15;
|
|
|
|
m_last_val = data & 0x0b;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*-------------------------------------------------
|
|
Carts with Flash RAM
|
|
-------------------------------------------------*/
|
|
|
|
void gba_rom_flash_device::device_add_mconfig(machine_config &config)
|
|
{
|
|
PANASONIC_MN63F805MNP(config, "flash");
|
|
}
|
|
|
|
|
|
READ32_MEMBER(gba_rom_flash_device::read_ram)
|
|
{
|
|
uint32_t rv = 0;
|
|
|
|
offset &= m_flash_mask;
|
|
|
|
if (mem_mask & 0xff)
|
|
rv |= m_flash->read(space, offset * 4);
|
|
if (mem_mask & 0xff00)
|
|
rv |= m_flash->read(space, (offset * 4) + 1) << 8;
|
|
if (mem_mask & 0xff0000)
|
|
rv |= m_flash->read(space, (offset * 4) + 2) << 16;
|
|
if (mem_mask & 0xff000000)
|
|
rv |= m_flash->read(space, (offset * 4) + 3) << 24;
|
|
|
|
return rv;
|
|
}
|
|
|
|
WRITE32_MEMBER(gba_rom_flash_device::write_ram)
|
|
{
|
|
offset &= m_flash_mask;
|
|
|
|
switch (mem_mask)
|
|
{
|
|
case 0xff:
|
|
m_flash->write(space, offset * 4, data & 0xff);
|
|
break;
|
|
case 0xff00:
|
|
m_flash->write(space, (offset * 4) + 1, (data >> 8) & 0xff);
|
|
break;
|
|
case 0xff0000:
|
|
m_flash->write(space, (offset * 4) + 2, (data >> 16) & 0xff);
|
|
break;
|
|
case 0xff000000:
|
|
m_flash->write(space, (offset * 4) + 3, (data >> 24) & 0xff);
|
|
break;
|
|
default:
|
|
fatalerror("Unknown mem_mask for GBA flash write %x\n", mem_mask);
|
|
}
|
|
}
|
|
|
|
void gba_rom_flash1m_device::device_add_mconfig(machine_config &config)
|
|
{
|
|
SANYO_LE26FV10N1TS(config, "flash");
|
|
}
|
|
|
|
|
|
READ32_MEMBER(gba_rom_flash1m_device::read_ram)
|
|
{
|
|
uint32_t rv = 0;
|
|
|
|
offset &= m_flash_mask;
|
|
|
|
if (mem_mask & 0xff)
|
|
rv |= m_flash->read(space, offset * 4);
|
|
if (mem_mask & 0xff00)
|
|
rv |= m_flash->read(space, (offset * 4) + 1) << 8;
|
|
if (mem_mask & 0xff0000)
|
|
rv |= m_flash->read(space, (offset * 4) + 2) << 16;
|
|
if (mem_mask & 0xff000000)
|
|
rv |= m_flash->read(space, (offset * 4) + 3) << 24;
|
|
|
|
return rv;
|
|
}
|
|
|
|
WRITE32_MEMBER(gba_rom_flash1m_device::write_ram)
|
|
{
|
|
offset &= m_flash_mask;
|
|
|
|
switch (mem_mask)
|
|
{
|
|
case 0xff:
|
|
m_flash->write(space, offset * 4, data & 0xff);
|
|
break;
|
|
case 0xff00:
|
|
m_flash->write(space, (offset * 4) + 1, (data >> 8) & 0xff);
|
|
break;
|
|
case 0xff0000:
|
|
m_flash->write(space, (offset * 4) + 2, (data >> 16) & 0xff);
|
|
break;
|
|
case 0xff000000:
|
|
m_flash->write(space, (offset * 4) + 3, (data >> 24) & 0xff);
|
|
break;
|
|
default:
|
|
fatalerror("Unknown mem_mask for GBA flash write %x\n", mem_mask);
|
|
}
|
|
}
|
|
|
|
// cart variants with additional S3511 RTC
|
|
|
|
uint16_t gba_rom_flash_rtc_device::gpio_dev_read(int gpio_dirs)
|
|
{
|
|
return 5 | (m_rtc->read_line() << 1);
|
|
}
|
|
|
|
void gba_rom_flash_rtc_device::gpio_dev_write(uint16_t data, int gpio_dirs)
|
|
{
|
|
m_rtc->write(data, gpio_dirs);
|
|
}
|
|
|
|
|
|
uint16_t gba_rom_flash1m_rtc_device::gpio_dev_read(int gpio_dirs)
|
|
{
|
|
return 5 | (m_rtc->read_line() << 1);
|
|
}
|
|
|
|
void gba_rom_flash1m_rtc_device::gpio_dev_write(uint16_t data, int gpio_dirs)
|
|
{
|
|
m_rtc->write(data, gpio_dirs);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
Carts with EEPROM
|
|
-------------------------------------------------*/
|
|
|
|
READ32_MEMBER(gba_rom_eeprom_device::read_ram)
|
|
{
|
|
// Larger games have smaller access to EERPOM content
|
|
if (m_rom_size > (16 * 1024 * 1024) && offset < 0xffff00/4)
|
|
return 0xffffffff;
|
|
|
|
return m_eeprom->read();
|
|
}
|
|
|
|
WRITE32_MEMBER(gba_rom_eeprom_device::write_ram)
|
|
{
|
|
// Larger games have smaller access to EEPROM content
|
|
if (m_rom_size > (16 * 1024 * 1024) && offset < 0xffff00/4)
|
|
return;
|
|
|
|
if (~mem_mask == 0x0000ffff)
|
|
data >>= 16;
|
|
|
|
m_eeprom->write(data);
|
|
}
|
|
|
|
READ32_MEMBER(gba_rom_eeprom64_device::read_ram)
|
|
{
|
|
// Larger games have smaller access to EERPOM content
|
|
if (m_rom_size > (16 * 1024 * 1024) && offset < 0xffff00/4)
|
|
return 0xffffffff;
|
|
|
|
return m_eeprom->read();
|
|
}
|
|
|
|
WRITE32_MEMBER(gba_rom_eeprom64_device::write_ram)
|
|
{
|
|
// Larger games have smaller access to EEPROM content
|
|
if (m_rom_size > (16 * 1024 * 1024) && offset < 0xffff00/4)
|
|
return;
|
|
|
|
if (~mem_mask == 0x0000ffff)
|
|
data >>= 16;
|
|
|
|
m_eeprom->write(data);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
Carts with EEPROM + Tilt Sensor
|
|
|
|
Note about the calibration: this can seem a bit
|
|
tricky at first, because the emulated screen
|
|
does not turn as the GBA would...
|
|
In order to properly calibrate the sensor, just
|
|
keep pressed right for a few seconds when requested
|
|
to calibrate right inclination (first calibration
|
|
screen in Yoshi Universal Gravitation) so to get the
|
|
full right range; then keep pressed for left for a
|
|
few seconds when requested to calibrate left
|
|
inclination (second calibration screen in Yoshi
|
|
Universal Gravitation) so to get the full left range
|
|
|
|
-------------------------------------------------*/
|
|
|
|
static INPUT_PORTS_START( yoshiug_tilt )
|
|
PORT_START("TILTX")
|
|
PORT_BIT( 0xfff, 0x3a0, IPT_AD_STICK_X ) PORT_MINMAX(0x2af,0x477) PORT_SENSITIVITY(0x30) PORT_KEYDELTA(0x50)
|
|
PORT_START("TILTY")
|
|
PORT_BIT( 0xfff, 0x3a0, IPT_AD_STICK_Y ) PORT_MINMAX(0x2c3,0x480) PORT_SENSITIVITY(0x30) PORT_KEYDELTA(0x50)
|
|
INPUT_PORTS_END
|
|
|
|
ioport_constructor gba_rom_yoshiug_device::device_input_ports() const
|
|
{
|
|
return INPUT_PORTS_NAME( yoshiug_tilt );
|
|
}
|
|
|
|
|
|
READ32_MEMBER(gba_rom_yoshiug_device::read_tilt)
|
|
{
|
|
switch (offset)
|
|
{
|
|
case 0x200/4:
|
|
if (ACCESSING_BITS_0_15)
|
|
return (m_xpos & 0xff);
|
|
break;
|
|
case 0x300/4:
|
|
if (ACCESSING_BITS_0_15)
|
|
return ((m_xpos >> 8) & 0x0f) | 0x80;
|
|
break;
|
|
case 0x400/4:
|
|
if (ACCESSING_BITS_0_15)
|
|
return (m_ypos & 0xff);
|
|
break;
|
|
case 0x500/4:
|
|
if (ACCESSING_BITS_0_15)
|
|
return ((m_ypos >> 8) & 0x0f);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return 0xffffffff;
|
|
}
|
|
|
|
WRITE32_MEMBER(gba_rom_yoshiug_device::write_tilt)
|
|
{
|
|
switch (offset)
|
|
{
|
|
case 0x000/4:
|
|
if (data == 0x55) m_tilt_ready = 1;
|
|
break;
|
|
case 0x100/4:
|
|
if (data == 0xaa)
|
|
{
|
|
m_xpos = m_tilt_x->read();
|
|
m_ypos = m_tilt_y->read();
|
|
m_tilt_ready = 0;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
Carts with EEPROM + S3511 RTC + Light Sensor
|
|
-------------------------------------------------*/
|
|
|
|
static INPUT_PORTS_START( boktai_sensor )
|
|
PORT_START("LIGHTSENSE")
|
|
PORT_CONFNAME( 0xff, 0xe8, "Light Sensor" )
|
|
PORT_CONFSETTING( 0xe8, "Complete Darkness" )
|
|
PORT_CONFSETTING( 0xe4, "10%" )
|
|
PORT_CONFSETTING( 0xdc, "20%" )
|
|
PORT_CONFSETTING( 0xd4, "30%" )
|
|
PORT_CONFSETTING( 0xc8, "40%" )
|
|
PORT_CONFSETTING( 0xb8, "50%" )
|
|
PORT_CONFSETTING( 0xa8, "60%" )
|
|
PORT_CONFSETTING( 0x98, "70%" )
|
|
PORT_CONFSETTING( 0x88, "80%" )
|
|
PORT_CONFSETTING( 0x68, "90%" )
|
|
PORT_CONFSETTING( 0x48, "Very Bright" )
|
|
INPUT_PORTS_END
|
|
|
|
|
|
ioport_constructor gba_rom_boktai_device::device_input_ports() const
|
|
{
|
|
return INPUT_PORTS_NAME( boktai_sensor );
|
|
}
|
|
|
|
uint16_t gba_rom_boktai_device::gpio_dev_read(int gpio_dirs)
|
|
{
|
|
int light = (gpio_dirs == 7 && m_counter >= m_sensor->read()) ? 1 : 0;
|
|
return 5 | (m_rtc->read_line() << 1) | (light << 3);
|
|
}
|
|
|
|
void gba_rom_boktai_device::gpio_dev_write(uint16_t data, int gpio_dirs)
|
|
{
|
|
m_rtc->write(data, gpio_dirs);
|
|
if (gpio_dirs == 7)
|
|
{
|
|
if (data & 2)
|
|
m_counter = 0;
|
|
|
|
if ((data & 1) && !(m_last_val & 1))
|
|
{
|
|
m_counter++;
|
|
if (m_counter == 0x100)
|
|
m_counter = 0;
|
|
}
|
|
|
|
m_last_val = data & 7;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------
|
|
Carts with 3D Matrix Memory controller
|
|
|
|
Used by Video carts with 64MB ROM chips
|
|
Emulation based on the reverse engineering efforts
|
|
by endrift
|
|
|
|
The Memory controller basically behaves like a DMA
|
|
chip by writing first source and destination address,
|
|
then the number of 512K blocks to copy and finally
|
|
by issuing the transfer command.
|
|
Disney Collection 2 carts uses command 0x01 to start
|
|
the transfer, other carts might use 0x11 but currently
|
|
they die before getting to the mapper communication
|
|
(CPU emulation issue? cart mapping issue? still unknown)
|
|
|
|
To investigate:
|
|
- why the other carts fail
|
|
- which addresses might be used by the mapper
|
|
(Disney Collection 2 uses 0x08800180-0x0880018f
|
|
but it might well be possible to issue commands
|
|
in an extended range...)
|
|
- which bus addresses can be used by the mapper
|
|
(currently we restrict the mapping in the range
|
|
0x08000000-0x09ffffff but maybe also the rest of
|
|
the cart "range" is accessible...)
|
|
-------------------------------------------------*/
|
|
|
|
WRITE32_MEMBER(gba_rom_3dmatrix_device::write_mapper)
|
|
{
|
|
//printf("mapper write 0x%.8X - 0x%X\n", offset, data); fflush(stdout);
|
|
switch (offset & 3)
|
|
{
|
|
case 0:
|
|
//printf("command: %08x\n", data); fflush(stdout);
|
|
if (data & 0x01) // transfer data
|
|
memcpy((uint8_t *)m_romhlp + m_dst, (uint8_t *)m_rom + m_src, m_nblock * 0x200);
|
|
else
|
|
printf("Unknown mapper command 0x%X\n", data);
|
|
break;
|
|
case 1:
|
|
//printf("m_src: %08x\n", data); fflush(stdout);
|
|
m_src = data & 0x3ffffff;
|
|
break;
|
|
case 2:
|
|
//printf("m_dst: %08x\n", data); fflush(stdout);
|
|
if (data >= 0xa000000)
|
|
{
|
|
printf("Unknown transfer destination 0x%X\n", data);
|
|
fflush(stdout);
|
|
}
|
|
m_dst = (data & 0x1ffffff);
|
|
break;
|
|
case 3:
|
|
default:
|
|
//printf("m_nblock: %08x\n", data); fflush(stdout);
|
|
m_nblock = data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Additional devices, to be moved to separate source files at a later stage
|
|
|
|
/*-------------------------------------------------
|
|
Seiko S-3511 RTC implementation
|
|
|
|
TODO: transform this into a separate device, using
|
|
also dirtc.cpp!
|
|
-------------------------------------------------*/
|
|
|
|
gba_s3511_device::gba_s3511_device(running_machine &machine) :
|
|
m_phase(S3511_RTC_IDLE),
|
|
m_machine(machine)
|
|
{
|
|
m_last_val = 0;
|
|
m_bits = 0;
|
|
m_command = 0;
|
|
m_data_len = 1;
|
|
m_data[0] = 0;
|
|
|
|
m_machine.save().save_item(m_phase, "GBA_RTC/m_phase");
|
|
m_machine.save().save_item(m_data, "GBA_RTC/m_data");
|
|
m_machine.save().save_item(m_last_val, "GBA_RTC/m_last_val");
|
|
m_machine.save().save_item(m_bits, "GBA_RTC/m_bits");
|
|
m_machine.save().save_item(m_command, "GBA_RTC/m_command");
|
|
m_machine.save().save_item(m_data_len, "GBA_RTC/m_data_len");
|
|
}
|
|
|
|
|
|
uint8_t gba_s3511_device::convert_to_bcd(int val)
|
|
{
|
|
return (((val % 100) / 10) << 4) | (val % 10);
|
|
}
|
|
|
|
void gba_s3511_device::update_time(int len)
|
|
{
|
|
system_time curtime;
|
|
m_machine.current_datetime(curtime);
|
|
|
|
if (len == 7)
|
|
{
|
|
m_data[0] = convert_to_bcd(curtime.local_time.year);
|
|
m_data[1] = convert_to_bcd(curtime.local_time.month + 1);
|
|
m_data[2] = convert_to_bcd(curtime.local_time.mday);
|
|
m_data[3] = convert_to_bcd(curtime.local_time.weekday);
|
|
m_data[4] = convert_to_bcd(curtime.local_time.hour);
|
|
m_data[5] = convert_to_bcd(curtime.local_time.minute);
|
|
m_data[6] = convert_to_bcd(curtime.local_time.second);
|
|
}
|
|
else if (len == 3)
|
|
{
|
|
m_data[0] = convert_to_bcd(curtime.local_time.hour);
|
|
m_data[1] = convert_to_bcd(curtime.local_time.minute);
|
|
m_data[2] = convert_to_bcd(curtime.local_time.second);
|
|
}
|
|
}
|
|
|
|
|
|
int gba_s3511_device::read_line()
|
|
{
|
|
int pin = 0;
|
|
switch (m_phase)
|
|
{
|
|
case S3511_RTC_DATAOUT:
|
|
//printf("mmm %d - %X - %d - %d\n", m_bits, m_data[m_bits >> 3], m_bits >> 3, BIT(m_data[m_bits >> 3], (m_bits & 7)));
|
|
pin = BIT(m_data[m_bits >> 3], (m_bits & 7));
|
|
m_bits++;
|
|
if (m_bits == 8 * m_data_len)
|
|
{
|
|
//for (int i = 0; i < m_data_len; i++)
|
|
// printf("RTC DATA OUT COMPLETE %X (reg %d) \n", m_data[i], i);
|
|
m_bits = 0;
|
|
m_phase = S3511_RTC_IDLE;
|
|
}
|
|
break;
|
|
}
|
|
return pin;
|
|
}
|
|
|
|
|
|
void gba_s3511_device::write(uint16_t data, int gpio_dirs)
|
|
{
|
|
// printf("gpio_dev_write data %X\n", data);
|
|
if (m_phase == S3511_RTC_IDLE && (m_last_val & 5) == 1 && (data & 5) == 5)
|
|
{
|
|
m_phase = S3511_RTC_COMMAND;
|
|
m_bits = 0;
|
|
m_command = 0;
|
|
}
|
|
else
|
|
{
|
|
// if (m_phase == 3)
|
|
// printf("RTC command OK\n");
|
|
if (!(m_last_val & 1) && (data & 1))
|
|
{
|
|
// bit transfer
|
|
m_last_val = data & 0xff;
|
|
switch (m_phase)
|
|
{
|
|
case S3511_RTC_DATAIN:
|
|
if (!BIT(gpio_dirs, 1))
|
|
{
|
|
m_data[m_bits >> 3] = (m_data[m_bits >> 3] >> 1) | ((data << 6) & 0x80);
|
|
m_bits++;
|
|
if (m_bits == 8 * m_data_len)
|
|
{
|
|
//for (int i = 0; i < m_data_len; i++)
|
|
// printf("RTC DATA IN COMPLETE %X (reg %d) \n", m_data[i], i);
|
|
m_bits = 0;
|
|
m_phase = S3511_RTC_IDLE;
|
|
}
|
|
}
|
|
break;
|
|
case S3511_RTC_DATAOUT:
|
|
break;
|
|
case S3511_RTC_COMMAND:
|
|
m_command |= (BIT(data, 1) << (7 - m_bits));
|
|
m_bits++;
|
|
if (m_bits == 8)
|
|
{
|
|
m_bits = 0;
|
|
//printf("RTC command %X ENTERED!!!\n", m_command);
|
|
switch (m_command)
|
|
{
|
|
case 0x60:
|
|
// reset?
|
|
m_phase = S3511_RTC_IDLE;
|
|
m_bits = 0;
|
|
break;
|
|
case 0x62:
|
|
m_phase = S3511_RTC_DATAIN;
|
|
m_data_len = 1;
|
|
break;
|
|
case 0x63:
|
|
m_data_len = 1;
|
|
m_data[0] = 0x40;
|
|
m_phase = S3511_RTC_DATAOUT;
|
|
break;
|
|
case 0x64:
|
|
break;
|
|
case 0x65:
|
|
m_data_len = 7;
|
|
update_time(m_data_len);
|
|
m_phase = S3511_RTC_DATAOUT;
|
|
break;
|
|
case 0x67:
|
|
m_data_len = 3;
|
|
update_time(m_data_len);
|
|
m_phase = S3511_RTC_DATAOUT;
|
|
break;
|
|
default:
|
|
printf("Unknown RTC command %02X\n", m_command);
|
|
m_phase = S3511_RTC_IDLE;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case S3511_RTC_IDLE:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
m_last_val = data & 0xff;
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
GBA EEPROM Device
|
|
|
|
TODO: can this sketchy EEPROM device be merged
|
|
with the core implementation?
|
|
-------------------------------------------------*/
|
|
|
|
//
|
|
|
|
gba_eeprom_device::gba_eeprom_device(running_machine &machine, uint8_t *eeprom, uint32_t size, int addr_bits) :
|
|
m_state(EEP_IDLE),
|
|
m_machine(machine)
|
|
{
|
|
m_data = eeprom;
|
|
m_data_size = size;
|
|
m_addr_bits = addr_bits;
|
|
|
|
m_machine.save().save_item(m_state, "GBA_EEPROM/m_state");
|
|
m_machine.save().save_item(m_command, "GBA_EEPROM/m_command");
|
|
m_machine.save().save_item(m_count, "GBA_EEPROM/m_count");
|
|
m_machine.save().save_item(m_addr, "GBA_EEPROM/m_addr");
|
|
m_machine.save().save_item(m_bits, "GBA_EEPROM/m_bits");
|
|
m_machine.save().save_item(m_eep_data, "GBA_EEPROM/m_eep_data");
|
|
}
|
|
|
|
uint32_t gba_eeprom_device::read()
|
|
{
|
|
uint32_t out;
|
|
|
|
switch (m_state)
|
|
{
|
|
case EEP_IDLE:
|
|
return 0x00010001; // "ready"
|
|
|
|
case EEP_READFIRST:
|
|
m_count--;
|
|
|
|
if (!m_count)
|
|
{
|
|
m_count = 64;
|
|
m_bits = 0;
|
|
m_eep_data = 0;
|
|
m_state = EEP_READ;
|
|
}
|
|
break;
|
|
case EEP_READ:
|
|
if ((m_bits == 0) && (m_count))
|
|
{
|
|
if (m_addr >= m_data_size)
|
|
{
|
|
fatalerror("eeprom: invalid address (%x)\n", m_addr);
|
|
}
|
|
m_eep_data = m_data[m_addr];
|
|
//printf("EEPROM read @ %x = %x (%x)\n", m_addr, m_eep_data, (m_eep_data & 0x80) ? 1 : 0);
|
|
m_addr++;
|
|
m_bits = 8;
|
|
}
|
|
|
|
out = (m_eep_data & 0x80) ? 1 : 0;
|
|
out |= (out<<16);
|
|
m_eep_data <<= 1;
|
|
|
|
m_bits--;
|
|
m_count--;
|
|
|
|
if (!m_count)
|
|
{
|
|
m_state = EEP_IDLE;
|
|
}
|
|
|
|
return out;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void gba_eeprom_device::write(uint32_t data)
|
|
{
|
|
switch (m_state)
|
|
{
|
|
case EEP_IDLE:
|
|
if (data == 1)
|
|
m_state++;
|
|
break;
|
|
|
|
case EEP_COMMAND:
|
|
if (data == 1)
|
|
m_command = EEP_READFIRST;
|
|
else
|
|
m_command = EEP_WRITE;
|
|
m_state = EEP_ADDR;
|
|
m_count = m_addr_bits;
|
|
m_addr = 0;
|
|
break;
|
|
|
|
case EEP_ADDR:
|
|
m_addr <<= 1;
|
|
m_addr |= (data & 1);
|
|
m_count--;
|
|
if (!m_count)
|
|
{
|
|
m_addr *= 8; // each address points to 8 bytes
|
|
if (m_command == EEP_READFIRST)
|
|
m_state = EEP_AFTERADDR;
|
|
else
|
|
{
|
|
m_count = 64;
|
|
m_bits = 8;
|
|
m_state = EEP_WRITE;
|
|
m_eep_data = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EEP_AFTERADDR:
|
|
m_state = m_command;
|
|
m_count = 64;
|
|
m_bits = 0;
|
|
m_eep_data = 0;
|
|
if (m_state == EEP_READFIRST)
|
|
m_count = 4;
|
|
break;
|
|
|
|
case EEP_WRITE:
|
|
m_eep_data <<= 1;
|
|
m_eep_data |= (data & 1);
|
|
m_bits--;
|
|
m_count--;
|
|
|
|
if (m_bits == 0)
|
|
{
|
|
osd_printf_verbose("%s: EEPROM: %02x to %x\n", machine().describe_context().c_str(), m_eep_data, m_addr);
|
|
if (m_addr >= m_data_size)
|
|
fatalerror("eeprom: invalid address (%x)\n", m_addr);
|
|
|
|
m_data[m_addr] = m_eep_data;
|
|
m_addr++;
|
|
m_eep_data = 0;
|
|
m_bits = 8;
|
|
}
|
|
|
|
if (!m_count)
|
|
m_state = EEP_AFTERWRITE;
|
|
break;
|
|
|
|
case EEP_AFTERWRITE:
|
|
m_state = EEP_IDLE;
|
|
break;
|
|
}
|
|
}
|