// license:BSD-3-Clause // copyright-holders:Fabio Priuli /*********************************************************************************************************** NES/Famicom cartridge emulation for FFE PCBs Here we emulate the mappers used by available Far Front East copier hacked games [mappers 6, 8, 17] TODO: - investigate IRQ mechanism (current code is broken) - replace this with proper copier emulation, using disk images... ***********************************************************************************************************/ #include "emu.h" #include "legacy.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 //------------------------------------------------- DEFINE_DEVICE_TYPE(NES_FFE3, nes_ffe3_device, "nes_ff3", "NES Cart FFE-3 PCB") DEFINE_DEVICE_TYPE(NES_FFE4, nes_ffe4_device, "nes_ff4", "NES Cart FFE-4 PCB") DEFINE_DEVICE_TYPE(NES_FFE8, nes_ffe8_device, "nes_ff8", "NES Cart FFE-8 PCB") nes_ffe3_device::nes_ffe3_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : nes_nrom_device(mconfig, NES_FFE3, tag, owner, clock) { } nes_ffe4_device::nes_ffe4_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : nes_nrom_device(mconfig, type, tag, owner, clock), m_irq_count(0), m_irq_enable(0), irq_timer(nullptr), m_latch(0), m_exram_enabled(0), m_exram_bank(0) { } nes_ffe4_device::nes_ffe4_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : nes_ffe4_device(mconfig, NES_FFE4, tag, owner, clock) { } nes_ffe8_device::nes_ffe8_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : nes_ffe4_device(mconfig, NES_FFE8, tag, owner, clock) { } void nes_ffe3_device::device_start() { common_start(); } void nes_ffe3_device::pcb_reset() { m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; prg32(0); chr8(0, m_chr_source); } void nes_ffe4_device::device_start() { common_start(); irq_timer = timer_alloc(TIMER_IRQ); irq_timer->adjust(attotime::zero, 0, clocks_to_attotime(1)); save_item(NAME(m_exram)); save_item(NAME(m_exram_enabled)); save_item(NAME(m_exram_bank)); save_item(NAME(m_irq_enable)); save_item(NAME(m_irq_count)); save_item(NAME(m_latch)); } void nes_ffe4_device::pcb_reset() { m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; prg16_89ab(0); prg16_cdef(7); chr8(0, m_chr_source); m_exram_enabled = 0; m_exram_bank = 0; m_latch = 0; m_irq_enable = 0; m_irq_count = 0; } void nes_ffe8_device::pcb_reset() { m_chr_source = m_vrom_chunks ? CHRROM : CHRRAM; prg16_89ab(0); prg16_cdef(0xff); chr8(0, m_chr_source); // extra vram is not used by this board, so these will remain always zero m_exram_enabled = 0; m_exram_bank = 0; m_latch = 0; m_irq_enable = 0; m_irq_count = 0; } /*------------------------------------------------- mapper specific handlers -------------------------------------------------*/ /*------------------------------------------------- Mapper 8 Known Boards: FFE3 Copier Board Games: Hacked versions of games In MESS: Supported? (I have no games to test this) -------------------------------------------------*/ void nes_ffe3_device::write_h(offs_t offset, uint8_t data) { LOG_MMC(("mapper8 write_h, offset: %04x, data: %02x\n", offset, data)); chr8(data & 0x07, CHRROM); prg16_89ab(data >> 3); } /*------------------------------------------------- Mapper 6 Known Boards: FFE4 Copier Board Games: Hacked versions of games In MESS: Supported? Not sure if we could also have ExRAM or not... However, priority is pretty low for this mapper. -------------------------------------------------*/ void nes_ffe4_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { if (id == TIMER_IRQ) { if (m_irq_enable) { if (m_irq_count == 0xffff) { set_irq_line(ASSERT_LINE); m_irq_count = 0; m_irq_enable = 0; } else m_irq_count++; } } } void nes_ffe4_device::write_l(offs_t offset, uint8_t data) { LOG_MMC(("mapper6 write_l, offset: %04x, data: %02x\n", offset, data)); switch (offset) { case 0x1fe: m_latch = data & 0x80; set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW); break; case 0x1ff: set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); break; case 0x401: m_irq_enable = 0; set_irq_line(CLEAR_LINE); break; case 0x402: m_irq_count = (m_irq_count & 0xff00) | data; break; case 0x403: m_irq_enable = 1; m_irq_count = (m_irq_count & 0x00ff) | (data << 8); break; } } void nes_ffe4_device::chr_w(offs_t offset, uint8_t data) { int bank = offset >> 10; if (m_exram_enabled) m_exram[(m_exram_bank * 0x2000) + (bank * 0x400) + (offset & 0x3ff)] = data; if (m_chr_src[bank] == CHRRAM) m_chr_access[bank][offset & 0x3ff] = data; } uint8_t nes_ffe4_device::chr_r(offs_t offset) { int bank = offset >> 10; if (m_exram_enabled) return m_exram[(m_exram_bank * 0x2000) + (bank * 0x400) + (offset & 0x3ff)]; return m_chr_access[bank][offset & 0x3ff]; } void nes_ffe4_device::write_h(offs_t offset, uint8_t data) { LOG_MMC(("mapper6 write_h, offset: %04x, data: %02x\n", offset, data)); if (!m_latch) // when in "FFE mode" we are forced to use CHRRAM/EXRAM bank? { prg16_89ab(data >> 2); // This part is not fully documented, so we proceed a bit blindly... if ((data & 0x03) == 0) { m_exram_enabled = 0; chr8(0, CHRRAM); } else { m_exram_enabled = 1; m_exram_bank = data & 0x03; } } else // otherwise, we use CHRROM (shall we check if it's present?) chr8(data, CHRROM); } /*------------------------------------------------- Mapper 17 Known Boards: FFE8 Copier Board Games: Hacked versions of games In MESS: Partially Supported. -------------------------------------------------*/ void nes_ffe8_device::write_l(offs_t offset, uint8_t data) { LOG_MMC(("mapper17 write_l, offset: %04x, data: %02x\n", offset, data)); switch (offset) { case 0x1fe: set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HIGH : PPU_MIRROR_LOW); break; case 0x1ff: set_nt_mirroring(BIT(data, 4) ? PPU_MIRROR_HORZ : PPU_MIRROR_VERT); break; case 0x401: m_irq_enable = 0; set_irq_line(CLEAR_LINE); break; case 0x402: m_irq_count = (m_irq_count & 0xff00) | data; break; case 0x403: m_irq_enable = 1; m_irq_count = (m_irq_count & 0x00ff) | (data << 8); break; case 0x404: prg8_89(data); break; case 0x405: prg8_ab(data); break; case 0x406: prg8_cd(data); break; case 0x407: prg8_ef(data); break; case 0x410: case 0x411: case 0x412: case 0x413: case 0x414: case 0x415: case 0x416: case 0x417: chr1_x(offset & 7, data, CHRROM); break; } }