diff options
Diffstat (limited to 'src/emu/bus/snes/rom.c')
-rw-r--r-- | src/emu/bus/snes/rom.c | 517 |
1 files changed, 517 insertions, 0 deletions
diff --git a/src/emu/bus/snes/rom.c b/src/emu/bus/snes/rom.c new file mode 100644 index 00000000000..55f376ae856 --- /dev/null +++ b/src/emu/bus/snes/rom.c @@ -0,0 +1,517 @@ +/*********************************************************************************************************** + + Super NES/Famicom (LoROM) cartridge emulation (for SNES/SFC) + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "rom.h" + + +//------------------------------------------------- +// sns_rom_device - constructor +//------------------------------------------------- + +const device_type SNS_LOROM = &device_creator<sns_rom_device>; +const device_type SNS_LOROM_OBC1 = &device_creator<sns_rom_obc1_device>; +// LoROM pirate carts with protection +const device_type SNS_LOROM_POKEMON = &device_creator<sns_rom_pokemon_device>; +const device_type SNS_LOROM_TEKKEN2 = &device_creator<sns_rom_tekken2_device>; +const device_type SNS_LOROM_SOULBLAD = &device_creator<sns_rom_soulblad_device>; +const device_type SNS_LOROM_BANANA = &device_creator<sns_rom_banana_device>; +const device_type SNS_LOROM_BUGSLIFE = &device_creator<sns_rom_bugs_device>; +// LoROM pirate multicarts +const device_type SNS_LOROM_MCPIR1 = &device_creator<sns_rom_mcpirate1_device>; +const device_type SNS_LOROM_MCPIR2 = &device_creator<sns_rom_mcpirate2_device>; +const device_type SNS_LOROM_20COL = &device_creator<sns_rom_20col_device>; + + +sns_rom_device::sns_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_sns_cart_interface( mconfig, *this ) +{ +} + +sns_rom_device::sns_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, SNS_LOROM, "SNES Cart (LoROM)", tag, owner, clock, "sns_rom", __FILE__), + device_sns_cart_interface( mconfig, *this ) +{ +} + +sns_rom_obc1_device::sns_rom_obc1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sns_rom_device(mconfig, SNS_LOROM_OBC1, "SNES Cart (LoROM) + OBC-1", tag, owner, clock, "sns_rom_obc1", __FILE__) +{ +} + + + +// Pirate LoROM 'mappers' +sns_rom_pokemon_device::sns_rom_pokemon_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sns_rom_device(mconfig, SNS_LOROM_POKEMON, "SNES Pirate Carts with Protection", tag, owner, clock, "sns_rom_pokemon", __FILE__) +{ +} + +sns_rom_tekken2_device::sns_rom_tekken2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sns_rom_device(mconfig, SNS_LOROM_TEKKEN2, "SNES Tekken 2", tag, owner, clock, "sns_rom_tekken2", __FILE__) +{ +} + +sns_rom_soulblad_device::sns_rom_soulblad_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sns_rom_device(mconfig, SNS_LOROM_SOULBLAD, "SNES Soul Blade", tag, owner, clock, "sns_rom_soulblad", __FILE__) +{ +} + +sns_rom_banana_device::sns_rom_banana_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sns_rom_device(mconfig, SNS_LOROM_BANANA, "SNES Banana de Pijamas", tag, owner, clock, "sns_rom_banana", __FILE__) +{ +} + +sns_rom_bugs_device::sns_rom_bugs_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sns_rom_device(mconfig, SNS_LOROM_BUGSLIFE, "SNES A Bug's Life", tag, owner, clock, "sns_rom_bugslife", __FILE__) +{ +} + +// Multigame LoROM 'mappers' +sns_rom_mcpirate1_device::sns_rom_mcpirate1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sns_rom_device(mconfig, SNS_LOROM_MCPIR1, "SNES Pirate Multigame Carts Type 1", tag, owner, clock, "sns_rom_mcpirate1", __FILE__) +{ +} + +sns_rom_mcpirate2_device::sns_rom_mcpirate2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sns_rom_device(mconfig, SNS_LOROM_MCPIR2, "SNES Pirate Multigame Carts Type 2", tag, owner, clock, "sns_rom_mcpirate2", __FILE__) +{ +} + +sns_rom_20col_device::sns_rom_20col_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sns_rom_device(mconfig, SNS_LOROM_20COL, "SNES Super 20 Collection", tag, owner, clock, "sns_rom_20col", __FILE__) +{ +} + + +void sns_rom_device::device_start() +{ +} + +void sns_rom_obc1_device::device_start() +{ + save_item(NAME(m_ram)); + save_item(NAME(m_address)); + save_item(NAME(m_offset)); + save_item(NAME(m_shift)); +} + +void sns_rom_obc1_device::device_reset() +{ + memset(m_ram, 0xff, sizeof(m_ram)); + // or from rom? + m_offset = (m_ram[0x1ff5] & 0x01) ? 0x1800 : 0x1c00; + m_address = (m_ram[0x1ff6] & 0x7f); + m_shift = (m_ram[0x1ff6] & 0x03) << 1; +} + + +void sns_rom_pokemon_device::device_start() +{ + save_item(NAME(m_latch)); +} + +void sns_rom_pokemon_device::device_reset() +{ + m_latch = 0; +} + +void sns_rom_tekken2_device::device_start() +{ + save_item(NAME(m_prot)); +} + +void sns_rom_tekken2_device::device_reset() +{ + m_prot = 0; +} + +void sns_rom_bugs_device::device_start() +{ + save_item(NAME(m_latch)); +} + +void sns_rom_bugs_device::device_reset() +{ + memset(m_latch, 0, sizeof(m_latch)); +} + + + +void sns_rom_mcpirate1_device::device_start() +{ + m_base_bank = 0; + save_item(NAME(m_base_bank)); +} + +void sns_rom_mcpirate1_device::device_reset() +{ +} + +void sns_rom_mcpirate2_device::device_start() +{ + m_base_bank = 0; + save_item(NAME(m_base_bank)); +} + +void sns_rom_mcpirate2_device::device_reset() +{ +} + +void sns_rom_20col_device::device_start() +{ + m_base_bank = 4; + save_item(NAME(m_base_bank)); +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +READ8_MEMBER(sns_rom_device::read_l) +{ + return read_h(space, offset); +} + +READ8_MEMBER(sns_rom_device::read_h) +{ + int bank = offset / 0x10000; + return m_rom[rom_bank_map[bank] * 0x8000 + (offset & 0x7fff)]; +} + + + +// Lo-ROM + OBC-1 (used by Metal Combat - Falcon's Revenge) +// same as above but additional read/write handling for the add-on chip + +/*************************************************************************** + + Based on C++ implementation by Byuu in BSNES. + + Byuu's code is released under GNU General Public License + version 2 as published by the Free Software Foundation. + + The implementation below is released under the MAME license + for use in MAME, MESS and derivatives by permission of Byuu + + Copyright (for the implementation below) MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + + ***********************************************************************************************************/ + + +READ8_MEMBER( sns_rom_obc1_device::chip_read ) +{ + UINT16 address = offset & 0x1fff; + UINT8 value; + + switch (address) + { + case 0x1ff0: + value = m_ram[m_offset + (m_address << 2) + 0]; + break; + + case 0x1ff1: + value = m_ram[m_offset + (m_address << 2) + 1]; + break; + + case 0x1ff2: + value = m_ram[m_offset + (m_address << 2) + 2]; + break; + + case 0x1ff3: + value = m_ram[m_offset + (m_address << 2) + 3]; + break; + + case 0x1ff4: + value = m_ram[m_offset + (m_address >> 2) + 0x200]; + break; + + default: + value = m_ram[address]; + break; + } + + return value; +} + + +WRITE8_MEMBER( sns_rom_obc1_device::chip_write ) +{ + UINT16 address = offset & 0x1fff; + UINT8 temp; + + switch(address) + { + case 0x1ff0: + m_ram[m_offset + (m_address << 2) + 0] = data; + break; + + case 0x1ff1: + m_ram[m_offset + (m_address << 2) + 1] = data; + break; + + case 0x1ff2: + m_ram[m_offset + (m_address << 2) + 2] = data; + break; + + case 0x1ff3: + m_ram[m_offset + (m_address << 2) + 3] = data; + break; + + case 0x1ff4: + temp = m_ram[m_offset + (m_address >> 2) + 0x200]; + temp = (temp & ~(3 << m_shift)) | ((data & 0x03) << m_shift); + m_ram[m_offset + (m_address >> 2) + 0x200] = temp; + break; + + case 0x1ff5: + m_offset = (data & 0x01) ? 0x1800 : 0x1c00; + m_ram[address & 0x1fff] = data; + break; + + case 0x1ff6: + m_address = data & 0x7f; + m_shift = (data & 0x03) << 1; + m_ram[address & 0x1fff] = data; + break; + + default: + m_ram[address & 0x1fff] = data; + break; + } +} + + + +// Lo-ROM + Protection devices used in pirate carts + + +// Pokemon (and many others): a byte is written and a permutation of its bits must be returned. +// Address range for read/write depends on the game (check snes.xml) +READ8_MEMBER( sns_rom_pokemon_device::chip_read ) +{ + return BITSWAP8(m_latch,0,6,7,1,2,3,4,5); +} + +WRITE8_MEMBER( sns_rom_pokemon_device::chip_write ) +{ + m_latch = data; +} + + +// Tekken 2: It accesses the protection in a very strange way, always reading/writing the same data $f0 times, +// because each access must be repeated a couple of times to be registered (typically around 7-30 times) +// They probably used a microcontroller here. +// The protection itself is accessed in banks $80-$bf. Accessing (read/write, doesn't matter) adress lines +// A8,A9,A10 in these banks in a certain sequence makes the mc return a 4bit value. [d4s] +// Details on a possible algorythm behind the sequence of accesses were provided by nocash. Thanks! +void sns_rom_tekken2_device::update_prot(UINT32 offset) +{ + // accesses to [80-bf][8000-87ff] ranges update the protection value + offset &= 0x7ff; + + switch (offset & 0x700) + { + case 0x000: + m_prot = 0; + break; + case 0x100: + // used for read access + break; + case 0x200: // BIT 0 + m_prot |= 1; + break; + case 0x300: // BIT 1 + m_prot |= 2; + break; + case 0x400: // BIT 2 + m_prot |= 4; + break; + case 0x500: // BIT 3 + m_prot |= 8; + break; + case 0x600: // DIRECTION + m_prot |= 0x10; + break; + case 0x700: // FUNCTION + m_prot |= 0x20; + break; + } +} + +READ8_MEMBER( sns_rom_tekken2_device::chip_read ) +{ + update_prot(offset); + + if ((offset & 0x700) == 0x100) + { + if (BIT(m_prot, 5)) // FUNCTION = 1 means Shift + { + if (BIT(m_prot, 4)) // DIRECTION = 1 means Right + return (m_prot & 0x0f) >> 1; + else // DIRECTION = 0 means Left + return (m_prot & 0x0f) << 1; + } + else // FUNCTION = 0 means Add/Sub + { + if (BIT(m_prot, 4)) // DIRECTION = 1 means Minus + return (m_prot & 0x0f) - 1; + else // DIRECTION = 0 means Plus + return (m_prot & 0x0f) + 1; + } + } + + return 0xff; // should be open_bus +} + +WRITE8_MEMBER( sns_rom_tekken2_device::chip_write ) +{ + update_prot(offset); +} + + +// Soul Blade: Adresses $xxx0-$xxx3 in banks $80-$bf always read $55, $0f, $aa, $f0. +// Banks $c0-$ff return open bus. +READ8_MEMBER( sns_rom_soulblad_device::chip_read ) +{ + UINT8 value = 0; + offset &= 3; + switch (offset) + { + case 0: + default: + value = 0x55; + break; + case 1: + value = 0x0f; + break; + case 2: + value = 0xaa; + break; + case 3: + value = 0xf0; + break; + } + return value; +} + +// Multicart pirate banking emulation +// LoROM games, writes to [ff][ff00-ffff] control bankswitch +// The actual banks depends on the last 8bits of the address accessed. + +// Type 1: bits0-4 of the address are used as base bank (256KB chunks) +READ8_MEMBER(sns_rom_mcpirate1_device::read_l) +{ + return read_h(space, offset); +} + +READ8_MEMBER(sns_rom_mcpirate1_device::read_h) +{ + int bank = (offset / 0x10000) + (m_base_bank * 8); + return m_rom[rom_bank_map[bank] * 0x8000 + (offset & 0x7fff)]; +} + +WRITE8_MEMBER( sns_rom_mcpirate1_device::chip_write ) +{ + m_base_bank = offset & 0x1f; +// printf("offset %X data %X bank %X\n", offset, data, m_base_bank); +} + +// Type 2: bits0-3 & bit5 of the address are used as base bank (256KB chunks) +READ8_MEMBER(sns_rom_mcpirate2_device::read_l) +{ + return read_h(space, offset); +} + +READ8_MEMBER(sns_rom_mcpirate2_device::read_h) +{ + int bank = (offset / 0x10000) + (m_base_bank * 8); + return m_rom[rom_bank_map[bank] * 0x8000 + (offset & 0x7fff)]; +} + +WRITE8_MEMBER( sns_rom_mcpirate2_device::chip_write ) +{ + m_base_bank = (offset & 0x0f) | ((offset & 0x20) >> 1); +// printf("offset %X data %X bank %X\n", offset, data, m_base_bank); +} + +// Korean 20 in 1 collection with NES games +// - base bank is selected (in 32KB chunks) by bits 0-4 of data written at 0x808000 +// - bits 6-7 seem related to prg size: 0x00 means 4*32KB, 0xc0 means 2*32KB, 0x80 means 1*32KB +// (they are used to setup how large is the ROM to be accessed, games 15-20 don't work well if +// accesses in [01-3f] don't go to the only 32KB bank) +// - bit 5 is always 0 +// it's worth to notice that for FC games size of bank is twice the size of original FC PRG +READ8_MEMBER(sns_rom_20col_device::read_l) +{ + return read_h(space, offset); +} + +READ8_MEMBER(sns_rom_20col_device::read_h) +{ + int prg32k = (!BIT(m_base_bank, 6) && BIT(m_base_bank, 7)); + int bank = prg32k ? 0 : (offset / 0x10000); + return m_rom[((m_base_bank & 0x1f) + bank) * 0x8000 + (offset & 0x7fff)]; +} + +WRITE8_MEMBER( sns_rom_20col_device::chip_write ) +{ + // [#] game - written bank value + // [01] spartan x - c6 + // [02] smb - c8 + // [03] antarcitc adv - 8e + // [04] twinbee - ca + // [05] battle city - 8f + // [06] circus charlie - 90 + // [07] galaga - 91 + // [08] yie ar kungfu - 92 + // [09] star force - 93 + // [10] road fighter - 94 + // [11] pinball - 95 + // [12] bomberman - 96 + // [13] new tetris?? - 0 + // [14] arkanoid - cc + // [15] balloon fight - 97 + // [16] donkey kong - 98 + // [17] donkey kong 3 - 99 + // [18] donkey kong jr - 9a + // [19] mario bros - 9b + // [20] popeye - 9c + m_base_bank = data & 0xdf; +// printf("offset %X data %X bank %X\n", offset, data, m_base_bank); +} + + + +// Work in progress (probably very wrong) + +READ8_MEMBER( sns_rom_banana_device::chip_read ) +{ + return BITSWAP8(m_latch[0xf],0,6,7,1,2,3,4,5); +} + +WRITE8_MEMBER( sns_rom_banana_device::chip_write ) +{ +// printf("write addr %X data %X\n", offset, data); + m_latch[0xf] = data; +} + +READ8_MEMBER( sns_rom_bugs_device::chip_read ) +{ + return BITSWAP8(m_latch[offset & 0xff],0,6,7,1,2,3,4,5); +} + +WRITE8_MEMBER( sns_rom_bugs_device::chip_write ) +{ + m_latch[offset & 0xff] = data; +} |