diff options
author | 2015-11-08 12:56:12 +0100 | |
---|---|---|
committer | 2015-11-08 12:56:12 +0100 | |
commit | 7c19aac60e12d6f5ea301bdb34d7826a01e0b06f (patch) | |
tree | f310d86aa2c6bfc19d115307dedde4eb0cd52dad /src/devices/bus/nes/mmc1.cpp | |
parent | a57b46ae933badd7441ce1644711dbb851e2b504 (diff) |
Rename *.c -> *.cpp in our source (nw)
Diffstat (limited to 'src/devices/bus/nes/mmc1.cpp')
-rw-r--r-- | src/devices/bus/nes/mmc1.cpp | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/src/devices/bus/nes/mmc1.cpp b/src/devices/bus/nes/mmc1.cpp new file mode 100644 index 00000000000..ff31250ee19 --- /dev/null +++ b/src/devices/bus/nes/mmc1.cpp @@ -0,0 +1,374 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + + NES/Famicom cartridge emulation for Nintendo MMC-1 PCBs + + + Here we emulate the Nintendo SxROM / MMC-1 PCBs + older variants without WRAM protect bit + + Known issues on specific mappers: + + * 001 Yoshi flashes in-game. + * 001 Back to the Future have heavily corrupted graphics (since forever). + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "mmc1.h" + +#ifdef NES_PCB_DEBUG +#define VERBOSE 1 +#else +#define VERBOSE 0 +#endif + +#define LOG_MMC(x) do { if (VERBOSE) logerror x; } while (0) + + +//------------------------------------------------- +// constructor +//------------------------------------------------- + +const device_type NES_SXROM = &device_creator<nes_sxrom_device>; +const device_type NES_SOROM = &device_creator<nes_sorom_device>; +const device_type NES_SXROM_A = &device_creator<nes_sxrom_a_device>; +const device_type NES_SOROM_A = &device_creator<nes_sorom_a_device>; + + + +nes_sxrom_device::nes_sxrom_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) + : nes_nrom_device(mconfig, type, name, tag, owner, clock, shortname, source) +{ +} + +nes_sxrom_device::nes_sxrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_nrom_device(mconfig, NES_SXROM, "NES Cart SxROM (MMC-1) PCB", tag, owner, clock, "nes_sxrom", __FILE__) +{ +} + +nes_sorom_device::nes_sorom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sxrom_device(mconfig, NES_SOROM, "NES Cart SOROM (MMC-1) PCB", tag, owner, clock, "nes_sorom", __FILE__) +{ +} + +nes_sxrom_a_device::nes_sxrom_a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sxrom_device(mconfig, NES_SXROM_A, "NES Cart SxROM (MMC-1A) PCB", tag, owner, clock, "nes_sxrom_a", __FILE__) +{ +} + +nes_sorom_a_device::nes_sorom_a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nes_sxrom_device(mconfig, NES_SOROM_A, "NES Cart SOROM (MMC-1A) PCB", tag, owner, clock, "nes_sorom_a", __FILE__) +{ +} + + + +void nes_sxrom_device::device_start() +{ + common_start(); + save_item(NAME(m_latch)); + save_item(NAME(m_count)); + save_item(NAME(m_reg)); + save_item(NAME(m_reg_write_enable)); +} + +void nes_sxrom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_latch = 0; + m_count = 0; + m_reg[0] = 0x0f; + m_reg[1] = m_reg[2] = m_reg[3] = 0; + m_reg_write_enable = 1; + + set_nt_mirroring(PPU_MIRROR_HORZ); + set_chr(); + set_prg(); +} + +void nes_sorom_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_latch = 0; + m_count = 0; + m_reg[0] = 0x0f; + m_reg[1] = m_reg[2] = m_reg[3] = 0; + m_reg_write_enable = 1; + + set_nt_mirroring(PPU_MIRROR_HORZ); + set_chr(); + set_prg(); +} + +void nes_sorom_a_device::pcb_reset() +{ + m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; + + m_latch = 0; + m_count = 0; + m_reg[0] = 0x0f; + m_reg[1] = m_reg[2] = m_reg[3] = 0; + m_reg_write_enable = 1; + + set_nt_mirroring(PPU_MIRROR_HORZ); + set_chr(); + set_prg(); +} + + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + + +/*------------------------------------------------- + + SxROM (MMC1 based) board emulation + + iNES: mapper 1 (and 155 for the MMC1A variant which does not + have WRAM disable bit) + + -------------------------------------------------*/ + +TIMER_CALLBACK_MEMBER( nes_sxrom_device::resync_callback ) +{ + m_reg_write_enable = 1; +} + + +void nes_sxrom_device::set_prg() +{ + UINT8 prg_mode, prg_offset; + + prg_mode = m_reg[0] & 0x0c; + /* prg_mode&0x8 determines bank size: 32k (if 0) or 16k (if 1)? when in 16k mode, + prg_mode&0x4 determines which half of the PRG space we can swap: if it is 4, + m_reg[3] sets banks at 0x8000; if it is 0, m_reg[3] sets banks at 0xc000. */ + + prg_offset = m_reg[1] & 0x10; + /* In principle, m_reg[2]&0x10 might affect "extended" banks as well, when chr_mode=1. + However, quoting Disch's docs: When in 4k CHR mode, 0x10 in both $A000 and $C000 *must* be + set to the same value, or else pages will constantly be swapped as graphics render! + Hence, we use only m_reg[1]&0x10 for prg_offset */ + + switch (prg_mode) + { + case 0x00: + case 0x04: +// printf("PRG 32 bank %d \n", (prg_offset + m_reg[3]) >> 1); + prg32((prg_offset + m_reg[3]) >> 1); + break; + case 0x08: +// printf("PRG 16 bank %d (high) \n", prg_offset + m_reg[3]); + prg16_89ab(prg_offset + 0); + prg16_cdef(prg_offset + m_reg[3]); + break; + case 0x0c: +// printf("PRG 16 bank %d (low) \n", prg_offset + m_reg[3]); + prg16_89ab(prg_offset + m_reg[3]); + prg16_cdef(prg_offset + 0x0f); + break; + } +} + +void nes_sxrom_device::set_chr() +{ + UINT8 chr_mode = BIT(m_reg[0], 4); + + if (chr_mode) + { + chr4_0(m_reg[1] & 0x1f, m_chr_source); + chr4_4(m_reg[2] & 0x1f, m_chr_source); + } + else + chr8((m_reg[1] & 0x1f) >> 1, m_chr_source); +} + +// this allows for easier implementation of the NES-EVENT board used for Nintento World Championships +void nes_sxrom_device::update_regs(int reg) +{ + switch (reg) + { + case 0: + switch (m_reg[0] & 0x03) + { + case 0: set_nt_mirroring(PPU_MIRROR_LOW); break; + case 1: set_nt_mirroring(PPU_MIRROR_HIGH); break; + case 2: set_nt_mirroring(PPU_MIRROR_VERT); break; + case 3: set_nt_mirroring(PPU_MIRROR_HORZ); break; + } + set_chr(); + set_prg(); + break; + case 1: + set_chr(); + set_prg(); + break; + case 2: + set_chr(); + break; + case 3: + set_prg(); + break; + } +} + +WRITE8_MEMBER( nes_sxrom_device::write_h ) +{ + LOG_MMC(("sxrom write_h, offset: %04x, data: %02x\n", offset, data)); + + // There is only one latch and shift counter, shared amongst the 4 regs (testcase: Space Shuttle) + + /* here we would need to add an if(cpu_cycles_passed>1) test, and + if requirement is not met simply return without writing anything. + Some games (AD&D Hillsfar, Bill & Ted Excellent Adventure, Cosmic + Wars, Rocket Ranger, Sesame Street 123 and Snow Brothers) rely on + this behavior!! */ + if (!m_reg_write_enable) + return; + else + { + m_reg_write_enable = 0; + machine().scheduler().synchronize(timer_expired_delegate(FUNC(nes_sxrom_device::resync_callback),this)); + } + + if (data & 0x80) + { + m_count = 0; + m_latch = 0; + + // Set reg at 0x8000 to size 16k and lower half swap - needed for Robocop 3, Dynowars + m_reg[0] |= 0x0c; + set_prg(); + return; + } + + if (m_count < 5) + { + if (m_count == 0) m_latch = 0; + m_latch >>= 1; + m_latch |= (data & 0x01) ? 0x10 : 0x00; + m_count++; + } + + if (m_count == 5) + { + m_reg[(offset & 0x6000) >> 13] = m_latch; + update_regs((offset & 0x6000) >> 13); + m_count = 0; + } +} + +WRITE8_MEMBER(nes_sxrom_device::write_m) +{ + UINT8 bank = (m_reg[1] >> 2) & 3; + LOG_MMC(("sxrom write_m, offset: %04x, data: %02x\n", offset, data)); + + if (!BIT(m_reg[3], 4)) // WRAM enabled + { + if (!m_battery.empty()) + m_battery[((bank * 0x2000) + offset) & (m_battery.size() - 1)] = data; + if (!m_prgram.empty()) + m_prgram[((bank * 0x2000) + offset) & (m_prgram.size() - 1)] = data; + } +} + +READ8_MEMBER(nes_sxrom_device::read_m) +{ + UINT8 bank = (m_reg[1] >> 2) & 3; + LOG_MMC(("sxrom read_m, offset: %04x\n", offset)); + + if (!BIT(m_reg[3], 4)) // WRAM enabled + { + if (!m_battery.empty()) + return m_battery[((bank * 0x2000) + offset) & (m_battery.size() - 1)]; + if (!m_prgram.empty()) + return m_prgram[((bank * 0x2000) + offset) & (m_prgram.size() - 1)]; + } + + return m_open_bus; // open bus +} + +// SOROM has two RAM banks, the first is not battery backed up, the second is. +WRITE8_MEMBER(nes_sorom_device::write_m) +{ + UINT8 type = BIT(m_reg[0], 4) ? BIT(m_reg[1], 4) : BIT(m_reg[1], 3); + LOG_MMC(("sorom write_m, offset: %04x, data: %02x\n", offset, data)); + + if (!BIT(m_reg[3], 4)) // WRAM enabled + { + if (type) + m_battery[offset & (m_battery.size() - 1)] = data; + else + m_prgram[offset & (m_prgram.size() - 1)] = data; + } +} + +READ8_MEMBER(nes_sorom_device::read_m) +{ + UINT8 type = BIT(m_reg[0], 4) ? BIT(m_reg[1], 4) : BIT(m_reg[1], 3); + LOG_MMC(("sorom read_m, offset: %04x\n", offset)); + + if (!BIT(m_reg[3], 4)) // WRAM enabled + { + if (type) + return m_battery[offset & (m_battery.size() - 1)]; + else + return m_prgram[offset & (m_prgram.size() - 1)]; + } + + return m_open_bus; // open bus +} + +// MMC1A boards have no wram enable/disable bit +WRITE8_MEMBER(nes_sxrom_a_device::write_m) +{ + UINT8 bank = (m_reg[1] >> 2) & 3; + LOG_MMC(("sxrom_a write_m, offset: %04x, data: %02x\n", offset, data)); + + if (!m_battery.empty()) + m_battery[((bank * 0x2000) + offset) & (m_battery.size() - 1)] = data; + if (!m_prgram.empty()) + m_prgram[((bank * 0x2000) + offset) & (m_prgram.size() - 1)] = data; +} + +READ8_MEMBER(nes_sxrom_a_device::read_m) +{ + UINT8 bank = (m_reg[1] >> 2) & 3; + LOG_MMC(("sxrom_a read_m, offset: %04x\n", offset)); + + if (!m_battery.empty()) + return m_battery[((bank * 0x2000) + offset) & (m_battery.size() - 1)]; + if (!m_prgram.empty()) + return m_prgram[((bank * 0x2000) + offset) & (m_prgram.size() - 1)]; + + return m_open_bus; // open bus +} + +WRITE8_MEMBER(nes_sorom_a_device::write_m) +{ + UINT8 type = BIT(m_reg[0], 4) ? BIT(m_reg[1], 4) : BIT(m_reg[1], 3); + LOG_MMC(("sorom_a write_m, offset: %04x, data: %02x\n", offset, data)); + + if (type) + m_battery[offset & (m_battery.size() - 1)] = data; + else + m_prgram[offset & (m_prgram.size() - 1)] = data; +} + +READ8_MEMBER(nes_sorom_a_device::read_m) +{ + UINT8 type = BIT(m_reg[0], 4) ? BIT(m_reg[1], 4) : BIT(m_reg[1], 3); + LOG_MMC(("sorom_a read_m, offset: %04x\n", offset)); + + if (type) + return m_battery[offset & (m_battery.size() - 1)]; + else + return m_prgram[offset & (m_prgram.size() - 1)]; +} |